Memperluas model Pengguna dengan bidang khusus di Django

442

Apa cara terbaik untuk memperluas model Pengguna (dibundel dengan aplikasi otentikasi Django) dengan bidang khusus? Saya juga mungkin ingin menggunakan email sebagai nama pengguna (untuk tujuan otentikasi).

Saya sudah melihat beberapa cara untuk melakukannya, tetapi tidak dapat memutuskan mana yang terbaik.

Farinha
sumber
24
Sebagian besar jawaban sudah usang / usang. Silakan lihat stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
pengguna
@buffer - Pertanyaan pertama yang Anda tautkan (stackoverflow.com/a/22856042/781695) sekarang telah dihapus. Yang lain masih valid.
Tony
3
Untuk menggunakan email dan bukan nama pengguna untuk otentikasi, artikel ini bermanfaat.
Saya cukup terpesona melihat pertanyaan itu diajukan pada 2008 sebelum Django dirilis @Farinha
Tessaracter

Jawaban:

253

Cara paling tidak menyakitkan dan memang direkomendasikan Django untuk melakukan ini adalah melalui OneToOneField(User)properti.

Memperluas model Pengguna yang ada

...

Jika Anda ingin menyimpan informasi terkait User, Anda dapat menggunakan hubungan satu-ke-satu ke model yang berisi bidang untuk informasi tambahan. Model satu-ke-satu ini sering disebut model profil, karena mungkin menyimpan informasi terkait non-auth tentang pengguna situs.

Yang mengatakan, memperluas django.contrib.auth.models.Userdan menggantinya juga berfungsi ...

Mengganti model Pengguna khusus

Beberapa jenis proyek mungkin memiliki persyaratan otentikasi yang Usermodel bawaan Django tidak selalu sesuai. Misalnya, pada beberapa situs lebih masuk akal untuk menggunakan alamat email sebagai token identifikasi Anda alih-alih nama pengguna.

[Ed: Dua peringatan dan pemberitahuan diikuti , menyebutkan bahwa ini sangat drastis .]

Saya pasti akan tinggal jauh dari mengubah kelas Pengguna yang sebenarnya di pohon sumber Django Anda dan / atau menyalin dan mengubah modul auth.

Ryan Duffield
sumber
50
FYI metode baru yang direkomendasikan (1.0+) adalah OneToOneField (Pengguna) docs.djangoproject.com/en/dev/topics/auth/…
Dave Forgac
2
Shawn Rider dari PBS memberikan beberapa alasan bagus mengapa Anda tidak perlu memperpanjang django.contrib.auth.models.User. Gunakan OneToOneField (Pengguna) sebagai gantinya.
pydanny
2
user = models.ForeignKey(User, unique=True)apakah ini sama dengan user = models.OneToOneField(User)? Saya akan berpikir efek akhirnya sama? Tapi mungkin implementasi di backend berbeda.
Sam Stoelinga
7
Adakah yang bisa membuat tautan ke argumen / alasan Shawn Riders untuk itu?
Jeremy Blanchard
1
Berikut adalah beberapa info tambahan tentang memperluas model pengguna pada django 1.7 docs
Derek Adair
226

Catatan: jawaban ini sudah usang. lihat jawaban lain jika Anda menggunakan Django 1.7 atau lebih baru.

Beginilah cara saya melakukannya.

#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'

Ini akan membuat profil pengguna setiap kali pengguna disimpan jika itu dibuat. Anda kemudian dapat menggunakan

  user.get_profile().whatever

Berikut ini beberapa info lebih lanjut dari dokumen

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

Pembaruan: Harap perhatikan bahwa AUTH_PROFILE_MODULEsudah usang sejak v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module

kismis
sumber
6
Terima kasih atas contoh yang jelas, perhatikan bahwa def create_user .... bukan bagian dari kelas UserProfile dan harus disejajarkan ke kiri.
PhoebeB
5
Dengan solusi ini haruskah model lain ForeignKey to User atau UserProfile?
andrewrk
9
Model lain harus digunakan user = models.ForeignKey( User ), dan mengambil objek profil melalui user.get_profile(). Ingat untuk from django.contrib.admin.models import User.
Craig Trader
1
Dengan menggunakan metode ini, saya perlu berpisah ketika saya mengambil informasi yang biasa (nama, kata sandi) dan yang khusus atau ada cara untuk melakukannya sekaligus? Sama untuk pembuatan pengguna baru?
Martin Trigaux
5
Jawaban & komentar ini telah kedaluwarsa misalnya, AUTH_PROFILE_MODULE sudah tidak digunakan lagi,User
pengguna
196

Nah, beberapa waktu berlalu sejak 2008 dan sudah waktunya untuk beberapa jawaban segar. Sejak Django 1.5 Anda akan dapat membuat kelas Pengguna khusus. Sebenarnya, pada saat saya menulis ini, itu sudah digabung menjadi master, jadi Anda bisa mencobanya.

Ada beberapa informasi tentang hal itu dalam dokumen atau jika Anda ingin menggali lebih dalam, dalam komit ini .

Yang harus Anda lakukan adalah menambahkan AUTH_USER_MODELke pengaturan dengan jalur ke kelas pengguna khusus, yang meluas baik AbstractBaseUser(versi lebih dapat disesuaikan) atau AbstractUser(lebih kurang kelas Pengguna lama Anda dapat memperluas).

Untuk orang yang malas mengklik, berikut ini contoh kode (diambil dari dokumen ):

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
Ondrej Slinták
sumber
Fungsi create_user tampaknya tidak menyimpan nama pengguna, bagaimana bisa ?!
Orca
6
Karena dalam contoh ini, emailadalah nama pengguna.
Ondrej Slinták
5
Anda perlu menambahkan unique=Trueke bidang email untuk USERNAME_FIELDmenerimanya
Richard de Wit
Hai, saya mencoba membuat pengguna khusus seperti yang Anda katakan tetapi tidak dapat masuk menggunakan email dari pengguna kustom. tidakkah Anda keberatan mengatakannya mengapa?
Lionel
Saya benar-benar tidak dapat membantu Anda tanpa spesifik. Akan lebih baik jika Anda membuat pertanyaan baru.
Ondrej Slinták
47

Sejak Django 1.5 Anda dapat dengan mudah memperluas model pengguna dan menyimpan satu tabel pada database.

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"))

Anda juga harus mengkonfigurasinya sebagai kelas pengguna saat ini di file pengaturan Anda

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

Jika Anda ingin menambahkan banyak preferensi pengguna, opsi OneToOneField mungkin pilihan yang lebih baik.

Catatan untuk orang-orang yang mengembangkan perpustakaan pihak ketiga: jika Anda perlu mengakses kelas pengguna ingat bahwa orang-orang dapat mengubahnya. Gunakan pembantu resmi untuk mendapatkan kelas yang tepat

from django.contrib.auth import get_user_model

User = get_user_model()
Riccardo Galli
sumber
3
Jika Anda berencana menggunakan django_social_auth, saya sarankan menggunakan hubungan OneToOne. JANGAN gunakan metode ini atau itu akan mengacaukan migrasi Anda.
Nimo
@Nimo: Bisakah Anda menguraikan atau mengutip referensi
pengguna
@ buffer, sudah lama sekali, tapi saya pikir saya mencoba menggabungkan django_social_authplugin dan mendefinisikan AUTH_USER_MODEL ke pengguna auth sosial. Lalu, ketika saya menjalankan manage.py, migrasi itu mengacaukan aplikasi saya. Ketika sebaliknya saya menggunakan model pengguna autentikasi sosial sebagai hubungan OneToOne yang dijelaskan di sini: stackoverflow.com/q/10638293/977116
Nimo
3
Mungkin ada hubungannya dengan 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 schemaSumber: docs.djangoproject.com/en/dev/topics/auth/customizing/…
pengguna
23

Yang di bawah ini adalah pendekatan lain untuk memperluas Pengguna. Saya merasa itu lebih jelas, mudah, dapat dibaca kemudian di atas dua pendekatan.

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

Menggunakan pendekatan di atas:

  1. Anda tidak perlu menggunakan user.get_profile (). newattribute untuk mengakses informasi tambahan yang terkait dengan pengguna
  2. Anda bisa langsung mengakses atribut baru tambahan melalui user.newattribute
Rama Vadakattu
sumber
1
Saya suka pendekatan Scott jauh lebih baik, berdasarkan pada warisan objek Pengguna daripada langsung dari model. Adakah yang bisa mengatakan jika pendekatan ini tidak bijaksana?
BozoJoe
1
@BozoJoe - Saya baru saja mengalami masalah ini mengimpor data dump, yang tampaknya merupakan konsekuensi dari menggunakan metode ini: stackoverflow.com/questions/8840068/…
Ben Regenspan
15

Anda dapat Cukup memperluas profil pengguna dengan membuat entri baru setiap kali ketika pengguna dibuat dengan menggunakan Django posting menyimpan sinyal

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)

Ini secara otomatis akan membuat contoh karyawan ketika pengguna baru dibuat.

Jika Anda ingin memperluas model pengguna dan ingin menambahkan informasi lebih lanjut saat membuat pengguna, Anda dapat menggunakan django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ). Ini akan membuat formulir tambah pengguna dengan semua bidang yang ditentukan dalam model UserProfile.

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'),
]
Atul Yadav
sumber
Hai dan terima kasih atas jawaban ini. Saya masih berjuang untuk memahami bagaimana menghubungkan semuanya bersama di urls.py..any ada petunjuk?
sal
@sal Menambahkan contoh url, Anda dapat memeriksa sekarang
Atul Yadav
Terima kasih!!. Ini sangat membantu saya. Contoh yang bagus
Sunny Chaudhari
@AtulYadav .. apa versi Django yang Anda gunakan?
Rido
8

Memperluas Model Pengguna Django (UserProfile) seperti Pro

Saya menemukan ini sangat berguna: tautan

Ekstrak:

dari django.contrib.auth.models impor Pengguna

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
Massimo Variolo
sumber
2
Selesai Saya tidak mengerti mengapa -1. Lebih baik edit daripada downvote dalam kasus ini.
Massimo Variolo
3

Baru di Django 1.5, sekarang Anda dapat membuat Model Pengguna Kustom Anda sendiri (yang tampaknya merupakan hal yang baik untuk dilakukan dalam kasus di atas). Lihat 'Menyesuaikan otentikasi dalam Django'

Mungkin fitur baru paling keren pada rilis 1.5.

chhantyal
sumber
1
Ya memang. Tetapi berhati-hatilah bahwa seseorang harus menghindari ini kecuali perlu. Menerapkan sendiri karena alasan dalam pertanyaan ini sangat valid meskipun Anda merasa nyaman dengan konsekuensi yang didokumentasikan. Untuk sekadar menambahkan bidang, hubungan dengan model Pengguna biasa disarankan .
gertvdijk
2

Inilah yang saya lakukan dan menurut saya cara paling sederhana untuk melakukan ini. mendefinisikan manajer objek untuk model kustomisasi baru Anda kemudian mendefinisikan model Anda.

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

Jangan lupa menambahkan baris kode ini di settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Inilah yang saya lakukan dan selalu berhasil.

Milad Khodabandehloo
sumber
0

Pendekatan sederhana dan efektif adalah models.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
NeerajSahani
sumber