Kullanıcı modelini Django'daki özel alanlarla genişletme


442

Kullanıcı modelini (Django'nun kimlik doğrulama uygulamasıyla birlikte) özel alanlarla genişletmenin en iyi yolu nedir? Ayrıca, e-postayı kullanıcı adı olarak kullanmak istiyorum (kimlik doğrulama amacıyla).

Bunu yapmanın birkaç yolunu gördüm , ancak hangisinin en iyisi olduğuna karar veremiyorum.


24
Çoğu cevap eski / kullanımdan kaldırılmıştır. Lütfen bkz. Stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
kullanıcı

@buffer - Bağlantı kurduğunuz ilk soru (stackoverflow.com/a/22856042/781695) artık silindi. Diğerleri hala geçerlidir.
Tony

3
Kimlik doğrulaması için kullanıcı adı yerine e-posta kullanmak için bu makale kullanışlıdır.


Django'nun piyasaya sürülmesinden önce sorunun 2008'de sorulduğunu görmek beni oldukça etkiledi @Farinha
Tessaracter

Yanıtlar:


253

Bunu yapmanın en az acı veren ve gerçekten de Django tarafından önerilen yolu bir OneToOneField(User)özelliktir.

Mevcut Kullanıcı modelini genişletme

...

İle ilgili bilgileri saklamak Useristiyorsanız, ek bilgi için alanları içeren bir modelle bire bir ilişki kullanabilirsiniz. Bu bire bir model, genellikle bir site kullanıcısıyla ilgili kimlik doğrulama ile ilgili olmayan bilgileri depolayabileceğinden, profil modeli olarak adlandırılır.

Bununla birlikte, uzatmak django.contrib.auth.models.Userve yerine koymak da işe yarıyor ...

Özel bir Kullanıcı modelini değiştirme

Bazı proje türlerinin Django'nun yerleşik Usermodelinin her zaman uygun olmadığı kimlik doğrulama gereksinimleri olabilir . Örneğin, bazı sitelerde kullanıcı adı yerine kimlik belirteciniz olarak bir e-posta adresi kullanmak daha mantıklıdır.

[Ed: Bunun oldukça sert olduğunu belirten iki uyarı ve bir bildirim geliyor .]

Ben kesinlikle Django kaynak ağacınızdaki gerçek Kullanıcı sınıfını değiştirmek ve / veya kopyalama ve auth modülünü değiştirmek uzak kalmak istiyorum.


51
Yeni (1.0+) önerilen yöntem FYI OneToOneField (Kullanıcı) ' dır docs.djangoproject.com/en/dev/topics/auth/…
Dave Forgac

2
PBS Shawn Rider sen neden bazı gerçekten iyi nedenler verdi değil django.contrib.auth.models.User uzanır. Bunun yerine OneToOneField (Kullanıcı) kullanın.
pydanny

2
user = models.ForeignKey(User, unique=True)bu aynı user = models.OneToOneField(User)mı? Son etkinin aynı olduğunu düşünürdüm? Ama belki arka uçtaki uygulama farklıdır.
Sam Stoelinga

7
Bunun için Shawn Riders argümanına / muhakemesine bağlantı veren var mı?
Jeremy Blanchard

1
İşte Django 1.7 dokümanlar gibi kullanıcı modellerini uzanan ilgili bazı ek bilgiler ise
Derek Adair

226

Not: bu cevap kullanımdan kaldırıldı. Django 1.7 veya üstünü kullanıyorsanız diğer yanıtlara bakın.

Ben böyle yaparım.

#in models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save

class UserProfile(models.Model):  
    user = models.OneToOneField(User)  
    #other fields here

    def __str__(self):  
          return "%s's profile" % self.user  

def create_user_profile(sender, instance, created, **kwargs):  
    if created:  
       profile, created = UserProfile.objects.get_or_create(user=instance)  

post_save.connect(create_user_profile, sender=User) 

#in settings.py
AUTH_PROFILE_MODULE = 'YOURAPP.UserProfile'

Bu, bir kullanıcı her oluşturulduğunda kaydedildiğinde bir kullanıcı profili oluşturur. Sonra kullanabilirsiniz

  user.get_profile().whatever

İşte dokümanlardan biraz daha bilgi

http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users

Güncelleme: Lütfen AUTH_PROFILE_MODULEv1.5'ten beri kullanımdan kaldırıldığını unutmayın : https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module


6
Açık örnek için teşekkürler, def create_user .... KullanıcıProfile sınıfının bir parçası değildir ve sola hizalanmalıdır.
PhoebeB

5
Bu çözüm ile diğer modeller kullanıcı veya UserProfile ForeignKey gerekir?
andrewrk

9
Diğer modeller kullanarak user = models.ForeignKey( User )profil nesnesini kullanmalı ve almalıdır user.get_profile(). Unutmayın from django.contrib.admin.models import User.
Craig Trader

1
Bu yöntemi kullanarak, her zamanki bilgileri (ad, parola) ve özel bir bilgi aldığımda ayırmak gerekir veya bir kerede yapmak için bir yolu var mı? Yeni bir kullanıcı oluşturmak için aynı mı?
Martin Trigaux

5
Bu yanıt ve yorum güncelliğini yitirdi, örneğin AUTH_PROFILE_MODULE kullanımdan kaldırıldı,User
kullanıcı

196

2008'den beri biraz zaman geçti ve yeni bir cevap vermenin zamanı geldi. Django 1.5'ten beri özel Kullanıcı sınıfı oluşturabileceksiniz. Aslında, bunu yazdığım sırada, zaten usta ile birleştirildi, böylece deneyebilirsiniz.

Dokümanlarda ya da daha derinlemesine incelemek istiyorsanız, bu taahhütte bazı bilgiler var .

Tek yapmanız gereken AUTH_USER_MODEL, AbstractBaseUser(daha özelleştirilebilir sürüm) veya AbstractUser(genişletebileceğiniz daha fazla veya daha az eski Kullanıcı sınıfı ) özel kullanıcı sınıfına giden yolu içeren ayarlara eklemektir .

Tıklama tembel insanlar için, kod örneği ( dokümanlardan alınmıştır ):

from django.db import models
from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser
)


class MyUserManager(BaseUserManager):
    def create_user(self, email, date_of_birth, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=MyUserManager.normalize_email(email),
            date_of_birth=date_of_birth,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, date_of_birth, password):
        """
        Creates and saves a superuser with the given email, date of
        birth and password.
        """
        u = self.create_user(username,
                        password=password,
                        date_of_birth=date_of_birth
                    )
        u.is_admin = True
        u.save(using=self._db)
        return u


class MyUser(AbstractBaseUser):
    email = models.EmailField(
                        verbose_name='email address',
                        max_length=255,
                        unique=True,
                    )
    date_of_birth = models.DateField()
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['date_of_birth']

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin

Create_user işlevi kullanıcı adını saklamıyor gibi görünüyor, nasıl ?!
Orca

6
Çünkü bu örnekte emailkullanıcı adı.
Ondrej Slinták

5
Sen eklemeniz gerekir unique=Trueizin eposta alanına USERNAME_FIELDonu kabul
Richard de Wit

Merhaba, dediğin gibi özel kullanıcı oluşturmaya çalışıyordum, ancak özel kullanıcı e-postasının e-postasını kullanarak giriş yapamadım. neden olduğunu söylemek sakıncası olmaz mı?
Lionel

Özellikleri olmadan size gerçekten yardım edemem. Yeni bir soru oluştursaydınız daha iyi olur.
Ondrej Slinták

47

Django 1.5'ten beri kullanıcı modelini kolayca genişletebilir ve veritabanında tek bir tablo tutabilirsiniz.

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _

class UserProfile(AbstractUser):
    age = models.PositiveIntegerField(_("age"))

Ayrıca, ayarlar dosyanızda geçerli kullanıcı sınıfı olarak yapılandırmanız gerekir

# supposing you put it in apps/profiles/models.py
AUTH_USER_MODEL = "profiles.UserProfile"

Çok sayıda kullanıcı tercihi eklemek istiyorsanız OneToOneField seçeneği daha iyi bir seçim olabilir.

Üçüncü taraf kütüphaneleri geliştiren kişiler için bir not: Kullanıcı sınıfına erişmeniz gerekiyorsa, insanların kütüphaneyi değiştirebileceğini unutmayın. Doğru sınıfı almak için resmi yardımcıyı kullanın

from django.contrib.auth import get_user_model

User = get_user_model()

3
Kullanmayı planlıyorsanız, django_social_authOneToOne ilişkisi kullanmanızı öneririz. Bu yöntemi KULLANMAYIN yoksa taşıma işlemlerinizi bozar.
Nimo

@Nimo: Bir referans hazırlayabilir veya alıntı yapabilir misiniz
kullanıcı

@buffer, uzun zaman önceydi, ancak django_social_autheklentiyi birleştirmeyi ve AUTH_USER_MODEL'i sosyal kimlik kullanıcısına tanımlamayı denedim . Sonra, ben manage.py migra koştu benim app berbat. Bunun yerine, burada açıklanan OneToOne ilişkisi olarak sosyal kimlik kullanıcı modelini kullandığımda: stackoverflow.com/q/10638293/977116
Nimo 16

3
Muhtemelen Changing this setting after you have tables created is not supported by makemigrations and will result in you having to manually write a set of migrations to fix your schemaKaynak ile ilgilidir: docs.djangoproject.com/en/dev/topics/auth/customizing/…
kullanıcı


23

Aşağıda bir Kullanıcı genişletmek için başka bir yaklaşımdır. İki yaklaşımın üzerinde olduğundan daha net, kolay, okunabilir olduğunu düşünüyorum.

http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/

Yukarıdaki yaklaşımı kullanarak:

  1. kullanıcıyla ilgili ek bilgilere erişmek için user.get_profile (). newattribute kullanmanıza gerek yoktur
  2. ek yeni özelliklere doğrudan user.newattribute üzerinden erişebilirsiniz.

1
Scott'ın yaklaşımını doğrudan modelden ziyade User nesnesinin mirasına dayalı olarak çok daha iyi seviyorum. Herkes bu yaklaşımın akıllıca olmadığını söyleyebilir mi?
BozoJoe

1
@BozoJoe - Ben sadece bu yöntemi kullanmanın bir sonucu gibi görünen dökümü verilerini içe aktarma ile karşılaştım: stackoverflow.com/questions/8840068/…
Ben Regenspan

15

Bir kullanıcı her oluşturulduğunda Django posta kaydetme sinyallerini kullanarak yeni bir giriş oluşturarak kullanıcı profilini kolayca genişletebilirsiniz.

models.py

from django.db.models.signals import *
from __future__ import unicode_literals

class UserProfile(models.Model):

    user_name = models.OneToOneField(User, related_name='profile')
    city = models.CharField(max_length=100, null=True)

    def __unicode__(self):  # __str__
        return unicode(self.user_name)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        userProfile.objects.create(user_name=instance)

post_save.connect(create_user_profile, sender=User)

Bu, yeni bir kullanıcı oluşturulduğunda otomatik olarak bir çalışan örneği oluşturur.

Kullanıcı modelini genişletmek ve kullanıcı oluştururken daha fazla bilgi eklemek istiyorsanız django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ) kullanabilirsiniz. Bu, UserProfile modelinde tanımlanan tüm alanları içeren bir kullanıcı ekleme formu oluşturur.

models.py

from django.db.models.signals import *
from __future__ import unicode_literals

class UserProfile(models.Model):

    user_name = models.OneToOneField(User)
    city = models.CharField(max_length=100)

    def __unicode__(self):  # __str__
        return unicode(self.user_name)

forms.py

from django import forms
from django.forms import ModelForm
from betterforms.multiform import MultiModelForm
from django.contrib.auth.forms import UserCreationForm
from .models import *

class ProfileForm(ModelForm):

    class Meta:
        model = Employee
        exclude = ('user_name',)


class addUserMultiForm(MultiModelForm):
    form_classes = {
        'user':UserCreationForm,
        'profile':ProfileForm,
    }

views.py

from django.shortcuts import redirect
from .models import *
from .forms import *
from django.views.generic import CreateView

class AddUser(CreateView):
    form_class = AddUserMultiForm
    template_name = "add-user.html"
    success_url = '/your-url-after-user-created'

    def form_valid(self, form):
        user = form['user'].save()
        profile = form['profile'].save(commit=False)
        profile.user_name = User.objects.get(username= user.username)
        profile.save()
        return redirect(self.success_url)

addUser.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="." method="post">
            {% csrf_token %}
            {{ form }}     
            <button type="submit">Add</button>
        </form>
     </body>
</html>

urls.py

from django.conf.urls import url, include
from appName.views import *
urlpatterns = [
    url(r'^add-user/$', AddUser.as_view(), name='add-user'),
]

Merhaba ve bu cevap için teşekkürler. Hala hepsini urls.py'de nasıl birbirine bağlayacağımı anlamaya çalışıyorum.
sal

@sal Şimdi kontrol edebileceğiniz URL'ler örneği eklendi
Atul Yadav

Teşekkürler!!. Bana çok yardımcı oluyor. İyi örnek
Sunny Chaudhari

@AtulYadav .. Django'nun kullandığınız versiyonu nedir?
Rido

8

Django Kullanıcı Modelini (UserProfile) bir Uzman gibi genişletme

Bunu çok faydalı buldum: link

Bir özü:

django.contrib.auth.models ithalat Kullanıcıdan

class Employee(models.Model):
    user = models.OneToOneField(User)
    department = models.CharField(max_length=100)

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department

2
Bitti. Neden -1 olduğunu anlamıyorum. Bu durumda bir aşağı nottan daha iyi bir düzenleme.
Massimo Variolo

3

Django 1.5'te yeni olan, şimdi kendi Özel Kullanıcı Modelinizi oluşturabilirsiniz (bu, yukarıdaki durumda yapılması iyi bir şey gibi görünüyor). 'Django'da kimlik doğrulamayı özelleştirme' konusuna bakın.

Muhtemelen 1.5 sürümündeki en havalı yeni özellik.


1
Evet kesinlikle. Ancak, gerekmedikçe bundan kaçınmanız gerektiğine dikkat edin. Bu sorunun nedeni için kendiniz uygulamak, belgelenmiş sonuçlardan memnun olmanız durumunda tamamen geçerlidir. Sadece alan eklemek için normal Kullanıcı modeliyle bir ilişki önerilir .
gertvdijk

2

Yaptığım şey bu ve bence bunu yapmanın en basit yolu. yeni özelleştirilmiş modeliniz için bir nesne yöneticisi tanımlayın ve ardından modelinizi tanımlayın.

from django.db import models
from django.contrib.auth.models import PermissionsMixin, AbstractBaseUser, BaseUserManager

class User_manager(BaseUserManager):
    def create_user(self, username, email, gender, nickname, password):
        email = self.normalize_email(email)
        user = self.model(username=username, email=email, gender=gender, nickname=nickname)
        user.set_password(password)
        user.save(using=self.db)
        return user

    def create_superuser(self, username, email, gender, password, nickname=None):
        user = self.create_user(username=username, email=email, gender=gender, nickname=nickname, password=password)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user



  class User(PermissionsMixin, AbstractBaseUser):
    username = models.CharField(max_length=32, unique=True, )
    email = models.EmailField(max_length=32)
    gender_choices = [("M", "Male"), ("F", "Female"), ("O", "Others")]
    gender = models.CharField(choices=gender_choices, default="M", max_length=1)
    nickname = models.CharField(max_length=32, blank=True, null=True)

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    REQUIRED_FIELDS = ["email", "gender"]
    USERNAME_FIELD = "username"
    objects = User_manager()

    def __str__(self):
        return self.username

Bu kod satırını aşağıdakilere eklemeyi unutmayın settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Yaptığım şey bu ve her zaman işe yarıyor.



0

Basit ve etkili yaklaşım models.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.