[PYTHON] Die Geschichte, einen süßen und schmerzhaften Blick auf benutzerdefinierte Benutzer auf Django zu werfen

Einführung

Ich möchte die Benutzerregistrierungs- / Authentifizierungs- / Anmeldefunktionen bei Django verbessern, und ich stecke fest, wenn ich fortfahre, also werde ich ein Protokoll führen.

Fazit

** Wenn Sie eine App mit Django entwickeln, definieren Sie Anforderungen und Design, bevor Sie ein Projekt starten **

** Sofern es nicht bereits durch Design usw. eingewebt wurde, ist es grundsätzlich besser, 1 Projekt und 1 Anwendung zu schützen, bis Django entwickelt werden kann. ** ** **

** Starten Sie das Projekt auch während der Entwicklung durch Studium erneut, ohne müde zu werden, und verwenden Sie es nicht wieder. ** ** **

Was ist passiert

Das Django-Tutorial ist beendet, also [dieser Artikel](https://qiita.com/okoppe8/items/54eb105c9c94c0960f14#%E6%89%8B%E9%A0%86%EF%BC%96%E3%83% 95% E3% 82% A9% E3% 83% BC% E3% 83% A0% E4% BD% 9C% E6% 88% 90) und [dieser Artikel](https://narito.ninja/blog/detail Basierend auf / 38 /) Ich habe verschiedene Dinge versucht, um eine Vorlage für eine TODO-Anwendung mit Benutzerregistrierungs- / Authentifizierungs- / Anmeldefunktionen in Django zu erstellen, aber als ich das Projekt des Tutorials umleitete, weil es problematisch war, das "Benutzermodell" und Ich halte mich an die Spezifikationen von "SuperUser".

Funktion zur Registrierung der Mitgliedschaft in Django

Verwenden Sie bei Verwendung der Mitgliedschaftsregistrierungsfunktion mit Django grundsätzlich das von Django bereitgestellte Benutzermodell. Solange Sie ein Framework einführen, werden Sie wahrscheinlich den vom Framework bereitgestellten Authentifizierungsmechanismus verwenden, es sei denn, mit einem Framework stimmt etwas nicht und es ist definitiv integriert. Einige haben Modelle und Methoden.

Das Problem von hier aus ist, dass Sie mit Django mehrere Anwendungen in einem Projektordner verwalten können und umgekehrt dieselbe Anwendung in mehreren Projektordnern installieren können. Tatsächlich kann jedoch nur ein Benutzermodell pro Projekt verwendet werden. Genau genommen empfiehlt Django dringend, ein benutzerdefiniertes Benutzermodell zu erstellen, das auf dem Benutzermodell für die Bereitstellung basiert und bei der ersten Migration erstellt werden muss. Zu diesem Zeitpunkt wird der in settings.py festgelegte Teil von AUTH_USER_MODEL festgelegt, um das Benutzermodell mit der Authentifizierungsfunktion zu behandeln. Es ist jedoch schneller, das Projekt neu zu starten, um dies später zu ändern. Es braucht Zeit und Mühe. Referenz

Mit anderen Worten, wenn Sie mehrere Apps mit einem "Projekt" verwalten möchten, müssen Sie entscheiden, welche Anwendung die Funktionen zur Authentifizierung haben soll, wenn Sie "Projekt" starten.

Ich folge dem nicht ... Mit anderen Worten, ich habe die Benutzerklasse im Tutorial definiert, aber ich wollte eine andere Anwendung erstellen und dort eine Authentifizierungsfunktion hinzufügen. Ich habe "project" gestartet und die "User" -Klasse so wie sie ist gestartet. Es bedeutet, dass es schwierig sein wird, zu machen.

Ich kann jedoch nicht anders, als das zu tun, was ich getan habe. Deshalb habe ich dieses Mal versucht herauszufinden, was mit dem vorhandenen "Projekt" getan werden kann, einschließlich der Bedeutung für spätere Studien.

Was ich getan habe

Zu diesem Zeitpunkt befinden sich das Modell, das das Modell auf der TODO-Anwendungsseite als Funktion verwendet, und das obige Erweiterungsmodell "Benutzer" (das auch den Datensatz für das Mail-Authentifizierungsflag überschreibt) in separaten Dateien.

Und natürlich muss das Herumspielen mit "models.py" "Migrationen durchführen" wiederholen, also lasst es uns vorsichtig machen.

Anpassung des vorhandenen Benutzermodells

2020-05-03_00h05_45.png

Entschuldigen Sie das Bild, denn das Zeichnen eines Verzeichnisdiagramms ist mühsam. Die Entwicklungsumgebung unter src ist "app", "polls" und "todo" im Projektordner. Definieren Sie das Benutzermodell in jeder Anwendung, und die Anwendung, die Sie dieses Mal erstellen möchten, ist "todo".

Zuerst werden wir den "Body" -Modellkörper anpassen. Referenz: Benutzermodell mit Django anpassen

app/models.py

from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin, UserManager
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.core.validators  import EmailValidator

class CustomUserManager(UserManager):

	use_in_migrations = True

	def _create_user(self,email,password, **extra_fields):
		if not email:
			raise ValueError('The given email must be set')

		email = self.normalize_email(email)
		user = self.model(email=email, **extra_fields)
		user.set_password(password)
		user.save(using=self._db)
		return user

	def create_user(self,email,password=None, **extra_fields):

		extra_fields.setdefault('is_staff', False)
		extra_fields.setdefault('is_superuser', False)
		return self._create_user(email, password, **extra_fields)

	def create_superuser(self, email, password, **extra_fields):
		extra_fields.setdefault('is_staff', True)
		extra_fields.setdefault('is_superuser', True)
		if extra_fields.get('is_staff') is not True:
			raise ValueError('Superuser must have is_staff=True.')
		if extra_fields.get('is_superuser') is not True:
			raise ValueError('Superuser must have is_superuser=True.')
		return self._create_user(emai, password, **extra_fields)

class User(AbstractBaseUser, PermissionsMixin):

	username = models.CharField(max_length=30, unique=True)
	email = models.EmailField(_('email address'), unique=True, validators=[EmailValidator('Invalid email address.')])
	first_name = models.CharField(_('first name'), max_length=30, blank=True)
	last_name = models.CharField(_('last name'), max_length=150, blank=True)


	is_staff = models.BooleanField(
		_('staff status'),
		default=False,
		help_text=_(
			'Designates whether this user can log into this admin site.'),
	)
	is_active = models.BooleanField(
		_('active'),
		default=True,
		help_text=_(
			'Designates whether this user should be treated as active.'
			'Unselect this instead of deleting accounts.'
		),
	)
	date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

	objects = CustomUserManager()

	EMAIL_FIELD = 'email'
	USERNAME_FIELD = 'email'
	REQUIRED_FIELD = []

	class Meta:
		verbose_name = _('user')
		verbose_name_plural = _('users')

	def email_user(self, subject, message, from_email=None, **kwargs):
		send_mail(subject, message, from_email, [self.email], **kwargs)


class SampleDB(models.Model):
	class Meta:
		db_table = 'sample_table' # tablename
		verbose_name_plural = 'sample_table' # Admintablename
	sample1 = models.IntegerField('sample1', null=True, blank=True) #Speichern Sie Nummern
	sample2 = models.CharField('sample2', max_length=255, null=True, blank=True)


class CustomUserManager(UserManager):

	use_in_migrations = True

	def _create_user(self,email,password, **extra_fields):
		if not email:
			raise ValueError('The given email must be set')

		email = self.normalize_email(email)
		user = self.model(email=email, **extra_fields)
		user.set_password(password)
		user.save(using=self._db)
		return user

	def create_user(self,email,password=None, **extra_fields):

		extra_fields.setdefault('is_staff', False)
		extra_fields.setdefault('is_superuser', False)
		return self._create_user(email, password, **extra_fields)

	def create_superuser(self, email, password, **extra_fields):
		extra_fields.setdefault('is_staff', True)
		extra_fields.setdefault('is_superuser', True)
		if extra_fields.get('is_staff') is not True:
			raise ValueError('Superuser must have is_staff=True.')
		if extra_fields.get('is_superuser') is not True:
			raise ValueError('Superuser must have is_superuser=True.')
		return self._create_user(emai, password, **extra_fields)

Dieser Teil überschreibt die Methoden zum Erstellen von Benutzern und Superusern. Mit anderen Worten, da dies der Teil ist, der sich auf die Anmeldung am Verwaltungsbildschirm von Django bezieht, habe ich den "UserManager" -Teil von "django.contrib.auth.models" kopiert und die erforderlichen Teile geändert. .. Die Quelle ist hier.


class User(AbstractBaseUser, PermissionsMixin):

	#Grundelement des Benutzermodells.
	username = models.CharField(max_length=30, unique=True)
	email = models.EmailField(_('email address'), unique=True, validators=[EmailValidator('Invalid email address.')])
	first_name = models.CharField(_('first name'), max_length=30, blank=True)
	last_name = models.CharField(_('last name'), max_length=150, blank=True)

	#admin Eine Methode, um festzustellen, ob ein Benutzer Zugriff auf eine Site hat
	is_staff = models.BooleanField(
		_('staff status'),
		default=False,
		help_text=_(
			'Designates whether this user can log into this admin site.'),
	)

	#Eine Methode, um festzustellen, ob ein Benutzer aktiv ist
	is_active = models.BooleanField(
		_('active'),
		default=True,
		help_text=_(
			'Designates whether this user should be treated as active.'
			'Unselect this instead of deleting accounts.'
		),
	)
	date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

	objects = CustomUserManager()

	#Einfach ausgedrückt werden das Mail-Dress-Feld, das als Benutzername verwendete Feld und das Feld, das beim Erstellen eines Superusers eingegeben werden muss, von oben angegeben.
	EMAIL_FIELD = 'email'
	USERNAME_FIELD = 'email'
	REQUIRED_FIELD = []

	class Meta:
		verbose_name = _('user')
		verbose_name_plural = _('users')

	#Methoden zum Versenden von E-Mails
	def email_user(self, subject, message, from_email=None, **kwargs):
		send_mail(subject, message, from_email, [self.email], **kwargs)

Dies erbt auch "AbstractBaseUser" von "django.contrib.auth.models". Da PermissionsMixin in der ursprünglichen Klasse vererbt ist, wird es auch hier vererbt. Als eine Rolle scheint es eine Sammlung von Methoden zu sein, die sich auf Autorität beziehen. models.BooleanField legt den Standardwert für BooleanField im Modellfeld fest und gibt True, False zurück. In der Referenzquelle möchte ich mich mit einer E-Mail-Adresse anstelle eines Benutzernamens anmelden. Daher lösche ich das Feld "Benutzername" und lasse das Feld "E-Mail" diese Rolle spielen. Die Einstellung ist "USERNAME_FIELD =" email ". Dieses Mal habe ich "Benutzername" verlassen, weil diese TODO-App für allgemeine Zwecke gedacht ist und ich sie basierend darauf umgestalten möchte, um etwas mit zusätzlichen Funktionen zu erstellen.

Apropos


def get_full_name(self):
        """Return the first_name plus the last_name, with a space in
        between."""
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

def get_short_name(self):
    """Return the short name for the user."""
    return self.first_name

#Definiert für den Fall, dass eine andere Anwendung auf das Attribut Benutzername zugreift und beim Zugriff die E-Mail-Adresse zurückgibt
@property
def username(self):
    return self.email



Elemente wie werden nach Bedarf hinzugefügt. Dieses Mal verwende ich nicht die Felder "Vorname" und "Nachname", daher habe ich sie nicht implementiert, aber ich denke, es wäre nützlich, wenn Sie ein Formular erstellen möchten, mit dem Sie Ihren Vor- und Nachnamen registrieren können.

Erstellen Sie ein neues Modell, das das Benutzermodell erweitert. Erstellen Sie es im Anwendungsordner und verwenden Sie "OneToOneField"

todo/models/account.py



from django.conf import settings
from django.db import models
from django.core import validators

class Activate(models.Model):
	user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
	key = models.CharField(max_length=255, blank=True, unique=True)
	expiration_date = models.DateTimeField(blank=True, null=True)

	def __str__(self):
		return self.key


	class Meta:
		verbose_name = 'E-Mail-Authentifizierungsflag'
		verbose_name_plural = 'E-Mail-Authentifizierungsflag'

Erstellen Sie dieses Mal ein Modell, das das Benutzermodell auf der TODO-Anwendungsseite erweitert. Dieses Mal definieren Sie zusätzlich zu den Informationen auf der Modellseite "Benutzer" die Felder für die E-Mail-Authentifizierung. OneToOneField ist eine Einstellung zum Definieren einer Eins-zu-Eins-Beziehung zu einem Modell. Da es nur ein Authentifizierungsflag für den Benutzer gibt, besteht eine Eins-zu-Eins-Beziehung. Es scheint, dass viele zu eins usw. durch "Fremdschlüssel" definiert werden.

Löschen Sie danach "models.py", um das geteilte Modell zu verpacken, erstellen Sie einen "Modelle" -Ordner, erstellen Sie "__init __. Py" darin und legen Sie das geteilte Modell in demselben Ordner ab. __init __. py definiert das Importieren des zu verpackenden Modells wie folgt:

todo/models/__init__.py



from .todo import Todo
from .account import Activate

Damit ist die Modellaufteilung abgeschlossen.

Ändern Sie "admin.py" auf der Seite, auf der sich das Benutzermodell befindet, um das angepasste Benutzermodell und das Modell widerzuspiegeln, das das Benutzermodell auf dem Verwaltungsbildschirm erweitert.

Wenn Sie danach "admin.py" ändern, handelt es sich um einen Absatz.

app/admin.py



from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.utils.translation import ugettext_lazy as _
from .models import User
#todo Anwendungskonto.Import Aktiviere Objekt von py
from todo.models.account import Activate
from.models import SampleDB

class MyUserChangeForm(UserChangeForm):
    class Meta:
        model = User
        fields = '__all__'


class MyUserCreationForm(UserCreationForm):
    class Meta:
        model = User
        fields = ('email',)

#Das zugehörige Mail-Authentifizierungsflag kann auf dem Verwaltungsbildschirm des Benutzermodells behandelt werden.
class ActivateInline(admin.StackedInline):
    model = Activate
    max_num = 1
    can_delete = False


class MyUserAdmin(UserAdmin):
    fieldsets = (
        (None, {'fields': ('username', 'password')}),
        (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
        (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                       'groups', 'user_permissions')}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2'),
        }),
    )
    form = MyUserChangeForm
    add_form = MyUserCreationForm
    list_display = ('username','email', 'first_name', 'last_name', 'is_staff')
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    search_fields = ('username','email', 'first_name', 'last_name')
    ordering = ('email',)
    #Geben Sie die ActivateInline-Klasse an
    inlines = [ActivateInline]


admin.site.register(User, MyUserAdmin)
admin.site.register(SampleDB)


Leihen Sie sich jetzt von django.contrib.auth.admin.py aus und überschreiben Sie diese, damit Sie Ihr benutzerdefiniertes Benutzermodell im Admin-Bildschirm sehen können. Das Herz ist der Teil "from todo.models.account import Activate".

todo/admin.py



from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.utils.translation import ugettext_lazy as _
from .models import Todo, Activate
from app.models import User

# Register your models here.

class TodoAdmin(admin.ModelAdmin):

	fields = ['todo_title', 'memo', 'datetime', 'tags']

	list_display = ('todo_title', 'datetime', 'created_at', 'tag_list')
	list_display_links = ('todo_title', 'datetime','tag_list')

	def get_queryset(self, request):
		return super().get_queryset(request).prefetch_related('tags')

	def tag_list(self, obj):
		return u", ".join(o.name for o in obj.tags.all())

	pass

admin.site.register(Todo,TodoAdmin)
admin.site.register(Activate)


Auf diese Weise kann das "Activate" -Modell auch auf der Seite der Aufgabenanwendung verwaltet werden. Es ist jedoch bequemer, es mit dem "User" -Modell zu verwalten, da es mit der Beziehung zusammenhängt. Importieren Sie das Modell daher mit "from todo.models.account import Activate". Hand, Dies bedeutet, dass auf der Seite der Benutzerklasse eine Inline-Klasse erstellt wird. Während wir mit der Arbeit fortfahren, werden wir uns vorerst jedes Mal darum kümmern, wenn ein Fehler in "USERNAME_FIELD =" email "" auftritt.

Referenz

Benutzermodell mit Django anpassen Django, Anpassung des Benutzermodells (OneToOne) Anmeldung mit Djangos benutzerdefiniertem Benutzermodell zulassen Erstellen Sie einen benutzerdefinierten Benutzer mit Django AbstractBaseUser Authentifizierungsmethode aus offiziellem Dokument anpassen Aus dem offiziellen Dokument django.contrib.auth

Recommended Posts

Die Geschichte, einen süßen und schmerzhaften Blick auf benutzerdefinierte Benutzer auf Django zu werfen
Eine Geschichte über Python Pop und Append
Eine Geschichte über die Einführung von Django anstelle von Rails bei einem jungen Start-up
Eine Geschichte, die von Go's globalen Variablen und ihrem Umfang abhängig ist
Eine Geschichte über die Implementierung eines Anmeldebildschirms mit Django
Eine Geschichte über das Ändern von Python und das Hinzufügen von Funktionen
Eine Geschichte über Kindergärten, Kindergärten und Kindergärten
Sehen Sie sich das Profiling und Dumping mit Dataflow an
Ein kurzer Blick auf Ihr Profil in der Django-App
Informationen zu benutzerdefinierten Django-Filterargumenten
Eine Geschichte von einer Person, die Django aus der interaktiven Python-Shell importieren und Dinge in der DB speichern wollte