So passen Sie das Benutzerprofil an, wenn Sie django-allauth verwenden

107

Ich habe ein Django-Projekt mit der Django-Allauth-App. Ich muss bei der Anmeldung zusätzliche Daten vom Benutzer sammeln. Ich bin hier auf eine ähnliche Frage gestoßen, aber leider hat niemand den Teil zur Profilanpassung beantwortet.

Gemäß der Dokumentation fürdjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

Eine Zeichenfolge, die auf eine benutzerdefinierte Formularklasse (z. B. ‘myapp.forms.SignupForm’) verweist, die während der Anmeldung verwendet wird, um den Benutzer um zusätzliche Eingaben zu bitten (z. B. Newsletter-Anmeldung, Geburtsdatum). Diese Klasse sollte eine ‘save’Methode implementieren , die den neu angemeldeten Benutzer als einzigen Parameter akzeptiert.

Ich bin neu in Django und kämpfe damit. Kann jemand ein Beispiel für eine solche benutzerdefinierte Formularklasse angeben? Muss ich auch eine Modellklasse mit einem Link zum Benutzerobjekt wie diesem hinzufügen ?

Shreyas
quelle

Antworten:

170

Angenommen, Sie möchten den Benutzer bei der Anmeldung nach seinem Vor- / Nachnamen fragen. Sie müssen diese Felder wie folgt in Ihre eigene Form einfügen:

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

Zeigen Sie dann in Ihren Einstellungen auf dieses Formular:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

Das ist alles.

pennersr
quelle
10
Vielen Dank. Es ist immer gut, vom Originalautor zu hören :). Muss ich eine zusätzliche Klasse erstellen, um diese Informationen zu speichern, oder kümmert sich allauth automatisch darum?
Shreyas
12
Das hängt tatsächlich von den Informationen ab, die Sie anfordern. In jedem Fall geht dies alles über den Rahmen hinaus. Wenn Sie wie im obigen Beispiel nach dem Vor- / Nachnamen fragen, benötigen Sie kein zusätzliches Modell und können die Dinge direkt in das Benutzermodell einfügen. Wenn Sie nach dem Geburtsdatum des Benutzers, seiner Lieblingsfarbe oder was auch immer fragen möchten, müssen Sie hierfür Ihr eigenes Profilmodell einrichten. Bitte schauen Sie hier, wie das geht: docs.djangoproject.com/de/dev/topics/auth/…
pennersr
6
Genau das habe ich gesucht - ein zusätzliches Feld wie die Lieblingsfarbe. Wenn ich beispielsweise an der Lieblingsfarbe interessiert bin, sollte ich meiner Meinung nach eine neue UserProfile-Klasse erstellen und dann den Benutzer als Eins-zu-Eins-Feld und die Lieblingsfarbe als zusätzliches Feld verwenden. Kann ich in diesem Fall weiterhin einen von Ihnen oben deklarierten SignUpForm-Typ (mit der bevorzugten Farbe) verwenden und die ACCOUNT_SIGNUP_FORM_CLASS daran anschließen, oder muss ich das Formular erstellen und das Speichern von Daten in meinem eigenen Code durchführen?
Shreyas
4
Sicher, der ACCOUNT_SIGNUP_FORM_CLASS-Mechanismus kann weiterhin verwendet werden. Sie müssen nur sicherstellen, dass die save () -Methode ordnungsgemäß implementiert ist, sodass die Lieblingsfarbe in einem beliebigen Modell gespeichert wird.
Pennersr
5
@pennersr - Wie kann ich dies ACCOUNT_SIGNUP_FORM_CLASSnach der ersten sozialen Anmeldung tun, um die benutzerdefinierten Benutzermodellfelder zu sammeln und zu speichern? Auch die Verwendung eines benutzerdefinierten Benutzermodells durch AUTH_USER_MODELÄnderungen von git: github.com/pennersr/django-allauth wird nicht in pypi hochgeladen.
Babu
23

Mit der von Pennersr vorgeschlagenen Lösung erhielt ich eine DeprecationWarning:

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

Dies liegt daran, dass ab Version 0.15 die Speichermethode zugunsten einer def-Anmeldemethode (Anfrage, Benutzer) veraltet ist.

Um dies zu lösen, sollte der Code des Beispiels folgendermaßen aussehen:

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
quelle
2
Die Antwort von @ pennsesr wurde jetzt bearbeitet, um sie signupanstelle von zu verwenden save.
Flimm
18

Hier ist, was für mich funktioniert hat, indem ich einige der anderen Antworten kombiniert habe (keine davon ist 100% vollständig und trocken).

In 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()

Und in settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

Auf diese Weise werden die Modellformulare so verwendet, dass sie TROCKEN sind, und die neuen def signup. Ich habe versucht zu setzen, 'myproject.myapp.forms.SignupForm'aber das führte irgendwie zu einem Fehler.

Howardwlo
quelle
Die Verwendung von 'yourapp.forms.SignupForm' anstelle von 'myproject.myapp.forms.SignupForm' funktionierte auch für mich
alpalalpal
6

@Shreyas: Die folgende Lösung ist möglicherweise nicht die sauberste, funktioniert aber. Bitte lassen Sie mich wissen, wenn Sie Vorschläge zur weiteren Bereinigung haben.

Um Informationen hinzuzufügen, die nicht zum Standardbenutzerprofil gehören, erstellen Sie zunächst ein Modell in yourapp / models.py. Lesen Sie die allgemeinen Django-Dokumente, um mehr darüber zu erfahren, aber im Grunde genommen:

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)

Erstellen Sie dann ein Formular in 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()
Kennzeichen
quelle
Genau das habe ich letztendlich für eine Django 2.0-App verwendet, auf der Wagtail CMS ausgeführt wird. Hat für die regelmäßige Anmeldung gearbeitet, aber weniger mit Social Auth, wie es scheint?
Kalob Taulien
Wie würde ich dieses zusätzliche Feld zur Administrationsseite für den Benutzer in Wagtail hinzufügen?
Joshua
5

In dein users/forms.pyhast du:

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

In settings.py setzen Sie:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

Auf diese Weise brechen Sie das DRY-Prinzip nicht durch die Definition von Feldern für Benutzermodelle.

Adam Dobrawy
quelle
4

Ich habe viele verschiedene Tutorials ausprobiert und in allen fehlt etwas, es wird unnötiger Code wiederholt oder es werden seltsame Dinge getan. Unten folgt meine Lösung, die alle Optionen verbindet, die ich gefunden habe. Es funktioniert. Ich habe es bereits in die Produktion aufgenommen, ABER es Ich kann mich immer noch nicht überzeugen, da ich erwarten würde, Vorname und Nachname innerhalb der Funktionen zu erhalten, die ich an Benutzer angehängt habe, um zu vermeiden, dass ein Profil innerhalb des Formulars erstellt wird, aber ich konnte es nicht, da ich denke, dass es Ihnen helfen wird.

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
quelle
Ironischerweise fehlen auch ein paar Dinge. Die Profilfelder haben weder Standardeinstellungen noch erlauben sie Null, sodass Ihr create_user_profileSignal beabsichtigt ausfällt. Zweitens können Sie dies auf ein Signal reduzieren, basierend auf created, insbesondere wenn Sie DRY sprechen. Und drittens führen Sie eine Profilspeicherung durch, indem Sie in Ihrer Ansicht user.save () aufrufen und das Profil dann erneut mit den tatsächlichen Daten speichern.
Melvyn
@ Melvyn Sollte es nicht fields = [...]mit eckigen Klammern anstelle von fields = (...) Klammern sein?
Ahtisham
Es kann, muss aber nicht sein. Es wird nur schreibgeschützt verwendet, um zu überprüfen, ob das Feld im Modell Teil des Formulars sein soll. Es kann sich also um eine Liste, ein Tupel oder eine Menge oder eine Ableitung davon handeln. Da Tupel nicht veränderbar sind, ist es sinnvoller, Tupel zu verwenden und versehentliche Mutationen zu verhindern. Aus Sicht der Leistung sind diese Sammlungen in der Praxis zu klein, um Auswirkungen zu haben. Sobald die Sammlung zu lang wird, kann es sinnvoll sein, excludestattdessen zu wechseln .
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'

Das ist es. (:

Milovan Tomašević
quelle
-10

Erstellen Sie ein Profilmodell mit dem Benutzer 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
quelle
3
Danke, aber ich denke nicht, dass dies alles ist, was benötigt wird. Meine Frage bezieht sich speziell auf die allauth App.
Shreyas