Bagaimana menyesuaikan profil pengguna saat menggunakan django-allauth

107

Saya memiliki proyek django dengan aplikasi django-allauth. Saya perlu mengumpulkan data tambahan dari pengguna saat mendaftar. Saya menemukan pertanyaan serupa di sini tetapi sayangnya, tidak ada yang menjawab bagian penyesuaian profil.

Sesuai dokumentasi yang disediakan untukdjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

String yang menunjuk ke kelas formulir kustom (misalnya ‘myapp.forms.SignupForm’) yang digunakan selama pendaftaran untuk meminta input tambahan dari pengguna (misalnya, pendaftaran buletin, tanggal lahir). Kelas ini harus mengimplementasikan sebuah ‘save’metode, menerima pengguna yang baru mendaftar sebagai satu-satunya parameter.

Saya baru mengenal Django dan sedang berjuang dengan ini. Dapatkah seseorang memberikan contoh kelas formulir kustom seperti itu? Apakah saya perlu menambahkan kelas model juga dengan tautan ke objek pengguna seperti ini ?

Shreyas
sumber

Jawaban:

170

Misalkan Anda ingin meminta nama depan / belakang pengguna saat mendaftar. Anda harus meletakkan bidang ini di formulir Anda sendiri, seperti:

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

Kemudian, dalam pengaturan Anda, arahkan ke formulir ini:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

Itu saja.

pennersr
sumber
10
Terima kasih. Senang rasanya mendengar dari penulis aslinya :). Apakah saya perlu membuat kelas tambahan untuk menyimpan info ini atau apakah allauth mengurusnya secara otomatis?
Shreyas
12
Itu sebenarnya tergantung pada informasi yang Anda tanyakan. Bagaimanapun, ini semua di luar cakupan allauth. Jika Anda meminta nama depan / belakang seperti pada contoh di atas, maka Anda tidak memerlukan model tambahan dan dapat langsung memasukkannya ke dalam model Pengguna. Jika Anda akan menanyakan tanggal lahir pengguna, warna favoritnya, atau apa pun, Anda perlu menyiapkan model profil Anda sendiri untuk ini. Silakan lihat di sini tentang cara melakukan ini: docs.djangoproject.com/en/dev/topics/auth/…
pennersr
6
Itulah yang saya cari - bidang tambahan seperti warna favorit. Jika saya tertarik untuk mengatakan, warna favorit, saya yakin saya harus membuat kelas UserProfile baru dan kemudian menggunakan User sebagai bidang satu lawan satu dan warna favorit sebagai bidang tambahan. Jika demikian, apakah saya masih dapat menggunakan jenis SignUpForm yang telah Anda nyatakan (dengan warna favorit) di atas dan menghubungkan ACCOUNT_SIGNUP_FORM_CLASS ke sana atau apakah saya perlu membuat formulir dan menangani penyimpanan data dalam kode saya sendiri?
Shreyas
4
Oke, mekanisme ACCOUNT_SIGNUP_FORM_CLASS masih bisa digunakan. Anda hanya perlu memastikan bahwa metode save () diterapkan dengan benar sehingga warna favorit disimpan dalam model apa pun yang Anda inginkan.
pennersr
5
@pennersr - Bagaimana cara merangkum ini ACCOUNT_SIGNUP_FORM_CLASSsetelah masuk sosial pertama untuk mengumpulkan dan menyimpan bidang model pengguna khusus? Juga penggunaan model pengguna ubahsuaian oleh AUTH_USER_MODELperubahan dari git: github.com/pennersr/django-allauth tidak diunggah di pypi.
Babu
23

Menggunakan solusi yang disarankan oleh pennersr, saya mendapatkan DeprecationWarning:

DeprecationWarning: The custom signup form must offer a def signup(self, request, user) method DeprecationWarning)

Ini karena mulai versi 0.15 , metode penyimpanan sudah tidak digunakan lagi untuk mendukung metode pendaftaran def (permintaan, pengguna).

Jadi untuk mengatasi ini, kode contohnya harus seperti ini:

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()
ferrangb.dll
sumber
2
Jawaban @ pennsesr sekarang telah diedit untuk digunakan signupsebagai pengganti save.
Flimm
18

Inilah yang berhasil bagi saya dengan menggabungkan beberapa jawaban lain (tidak ada yang 100% lengkap dan KERING).

Masuk yourapp/forms.py:

from django.contrib.auth import get_user_model
from django import forms

class SignupForm(forms.ModelForm):
    class Meta:
        model = get_user_model()
        fields = ['first_name', 'last_name']

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

Dan di settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

Dengan cara ini menggunakan bentuk model sehingga KERING, dan menggunakan yang baru def signup. Saya mencoba meletakkan 'myproject.myapp.forms.SignupForm'tetapi itu entah bagaimana menghasilkan kesalahan.

Howardwlo
sumber
menggunakan 'yourapp.forms.SignupForm' alih-alih 'myproject.myapp.forms.SignupForm' juga berfungsi untuk saya
alpalalpal
6

@ Shreyas: Solusi di bawah ini mungkin bukan yang terbersih, tetapi berhasil. Beri tahu saya jika Anda memiliki saran untuk membersihkannya lebih lanjut.

Untuk menambahkan informasi yang bukan milik profil pengguna default, pertama-tama buat model di yourapp / models.py. Bacalah dokumen umum django untuk mempelajarinya lebih lanjut, tetapi pada dasarnya:

from django.db import models

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile')
    organisation = models.CharField(organisation, max_length=100, blank=True)

Kemudian buat formulir di yourapp / forms.py:

from django import forms

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')
    organisation = forms.CharField(max_length=20, label='organisation')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        # Replace 'profile' below with the related_name on the OneToOneField linking back to the User model
        up = user.profile
        up.organisation = self.cleaned_data['organisation']
        user.save()
        up.save()
menandai
sumber
Inilah yang akhirnya saya gunakan untuk aplikasi Django 2.0 yang menjalankan Wagtail CMS. Bekerja untuk pendaftaran biasa, tetapi tampaknya kurang begitu dengan Social Auth?
Kalob Taulien
Bagaimana cara menambahkan bidang ekstra ini ke halaman admin untuk pengguna di Wagtail?
Joshua
5

Dalam users/forms.pyAnda memasukkan:

from django.contrib.auth import get_user_model
class SignupForm(forms.ModelForm):
    class Meta:
        model = get_user_model()
        fields = ['first_name', 'last_name']
    def save(self, user):
        user.save()

Di settings.py Anda meletakkan:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

Dengan cara ini Anda tidak melanggar prinsip KERING dengan banyaknya definisi bidang model Pengguna.

Adam Dobrawy
sumber
4

Saya telah mencoba banyak tutorial berbeda dan semuanya kehilangan sesuatu, mengulangi kode yang tidak perlu atau melakukan hal-hal aneh, di bawah ini ikuti solusi saya yang menggabungkan semua opsi yang saya temukan, berfungsi, saya sudah memasukkannya ke dalam produksi TETAPI masih belum meyakinkan saya karena saya berharap untuk menerima first_name dan last_name di dalam fungsi yang saya lampirkan ke Pengguna buat untuk menghindari membuat profil di dalam formulir tetapi saya tidak bisa, langsung saja saya pikir itu akan membantu Anda.

Models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    nationality = models.CharField(max_length=2, choices=COUNTRIES)
    gender = models.CharField(max_length=1, choices=GENDERS)

def __str__(self):
    return self.user.first_name


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

Forms.py

class SignupForm(forms.ModelForm):
    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)

    class Meta:
        model = Profile
        fields = ('first_name', 'last_name', 'nationality', 'gender')

    def signup(self, request, user):
        # Save your user
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

        user.profile.nationality = self.cleaned_data['nationality']
        user.profile.gender = self.cleaned_data['gender']
        user.profile.save()

Settings.py

ACCOUNT_SIGNUP_FORM_CLASS = 'apps.profile.forms.SignupForm'
Gregory
sumber
Ironisnya, ini juga kehilangan beberapa hal. Bidang Profil tidak memiliki default, juga tidak memungkinkan null, jadi create_user_profilesinyal Anda gagal sesuai desain. Kedua, Anda dapat mengurangi ini menjadi satu sinyal berdasarkan created, terutama saat berbicara KERING. Dan ketiga, Anda melakukan penyimpanan Profil dengan memanggil user.save () di tampilan Anda, lalu menyimpan profil itu lagi dengan data aktual.
Melvyn
@ Melvyn Bukankah seharusnya fields = [...]dengan tanda kurung siku alih-alih fields = (...)tanda kurung?
Ahtisham
Bisa, tapi tidak harus. Ini hanya digunakan hanya-baca untuk memeriksa apakah bidang pada Model harus menjadi bagian dari formulir. Jadi bisa berupa list, tuple atau set atau turunannya. Karena tupel tidak bisa berubah, lebih masuk akal untuk menggunakan tupel dan mencegah mutasi yang tidak disengaja. Dari perspektif kinerja, koleksi ini dalam praktiknya terlalu kecil untuk berdampak apa pun. Setelah koleksi menjadi terlalu panjang, mungkin masuk akal untuk beralih ke koleksi tersebut exclude.
Melvyn
0
#models.py

from django.conf import settings

class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    image = models.ImageField(default='users/default.png', upload_to='users')
    fields = models.ForeignKey('Field' ,null=True ,on_delete=models.SET_NULL)
    category = models.ForeignKey('Category' ,null=True ,on_delete=models.SET_NULL)
    description = models.TextField()
    interests = models.ManyToManyField('Interests')

    ...

   def save(self, *args, **kwargs):
       super().save(*args, **kwargs)

...

def userprofile_receiver(sender, instance, created, *args, **kwargs):
    if created:
        userprofile = UserProfile.objects.create(user=instance)
    else:
        instance.userprofile.save()

post_save.connect(userprofile_receiver, sender=settings.AUTH_USER_MODEL)



 #forms.py

 class SignupForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SignupForm, self).__init__(*args, **kwargs)
        self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter first name'})
        self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter last name'})

    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)

    interests  = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, help_text="Choose your interests", queryset=Interests.objects.all())

    image = forms.ImageField(help_text="Upload profile image ")
    fields = forms.ChoiceField(help_text="Choose your fields ")
    category = forms.ChoiceField(help_text="Choose your category")

    class Meta:
        model = UserProfile
        fields = ('first_name', 'last_name',  'name', 'image', 'fields', 'category', 'description', 'phone', 'facebook', 'twitter', 'skype', 'site', 'address', 'interests' ,'biography')
        widgets = {
             ...
            'description': forms.TextInput(attrs={'placeholder': 'Your description'}),
            'address': forms.TextInput(attrs={'placeholder': 'Enter address'}),
            'biography': forms.TextInput(attrs={'placeholder': 'Enter biography'}),
            ....
    }
    def signup(self, request, user):
        # Save your user
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

        user.userprofile.image = self.cleaned_data.get('image')
        user.userprofile.fields = self.cleaned_data['fields']
        user.userprofile.category = self.cleaned_data['category']
        user.userprofile.description = self.cleaned_data['description']
        interests = self.cleaned_data['interests']
        user.userprofile.interests.set(interests)
        user.userprofile.save()


# settings.py or base.py

ACCOUNT_SIGNUP_FORM_CLASS = 'nameApp.forms.SignupForm'

Hanya itu saja. (:

Milovan Tomašević
sumber
-10

Buat Model Profil dengan pengguna sebagai OneToOneField

class Profile(models.Model):
    user = models.OneToOneField(User, verbose_name=_('user'), related_name='profiles')
    first_name=models.CharField(_("First Name"), max_length=150)
    last_name=models.CharField(_("Last Name"), max_length=150)
    mugshot = ImageField(_('mugshot'), upload_to = upload_to, blank=True)
    phone= models.CharField(_("Phone Number"), max_length=100)
    security_question = models.ForeignKey(SecurityQuestion, related_name='security_question')
    answer=models.CharField(_("Answer"), max_length=200)
    recovery_number= models.CharField(_("Recovery Mobile Number"), max_length=100)
    city=models.ForeignKey(City,related_name='city', blank=True, null=True, help_text=_('Select your City'))
    location=models.ForeignKey(Country,related_name='location', blank=True, null=True, help_text=_('Select your Location'))
Jubin Thomas
sumber
3
Terima kasih, tapi saya rasa tidak hanya itu yang dibutuhkan. Pertanyaan saya secara khusus mengacu pada aplikasi allauth.
Shreyas