Несколько пользователей Django, бэкэнд и общие представления

Я пытаюсь изучить систему авторизации django, и у меня есть несколько вопросов.

Я хочу создать несколько пользователей с аутентификацией по электронной почте. Итак, я использовал AbtractBaseUser и AbtractBaseManager. Я прочитал документ django и этот вопрос на веб-сайте: Реализация нескольких типов пользователей с Джанго 1.5

и я хотел бы использовать его. Итак, я пытаюсь реализовать второй пункт: все поля для двух моих типов пользователей находятся в 1 модели, и с помощью переменной user_type я могу выбирать поля для отображения. Мой код ниже:

models.py

# -*- coding: utf-8 -*-

from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser
)

TYPE_USER = (
    ('student', _('Student')),
    ('employee', _('Employee')),
)

class MyuserManager(BaseUserManager):
    def create_user(self, email, last_name, first_name, date_of_birth, user_type, password=None):
        if not email:
            raise ValueError("users must have an email address")

        user = self.model(
            email         = self.normalize_email(email),
            last_name     = last_name,
            first_name    = first_name,
            date_of_birth = date_of_birth,
            user_type     = user_type,
        )

        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, last_name, first_name, date_of_birth, user_type):
        user = self.create_user(email,
            password      = password,
            date_of_birth = date_of_birth,
            last_name     = last_name,
            first_name    = first_name,
            user_type     = user_type,
        )
        user.is_admin = True
        user.is_staff = True
        user.save()
        return user

class MyUser(AbstractBaseUser):
    """Based model user"""
    email   = models.EmailField(
        verbose_name = 'email',
        max_length=255,
        unique=True,
    )
    last_name     = models.CharField(max_length=30)
    first_name    = models.CharField(max_length=30)
    date_of_birth = models.DateField()
    user_type     = models.CharField(_('Type'), choices=TYPE_USER, max_length=20, default='student')
    phone         = models.CharField(max_length=20, blank=True)
    friends       = models.ManyToManyField("self", blank=True)
    faculty       = models.ForeignKey(Faculty, null=True, blank=True)
    desk          = models.CharField(max_length=30, blank=True)
    campus        = models.ForeignKey(Campus, null=True, blank=True)
    job           = models.ForeignKey(Function, null=True, blank=True)
    cursus        = models.ForeignKey(Cursus, null=True, blank=True)
    year          = models.IntegerField(null=True, blank=True)
    is_active     = models.BooleanField(default=True)
    is_admin      = models.BooleanField(default=False)
    is_staff      = models.BooleanField(default=False)

    objects = MyuserManager()
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['last_name', 'first_name', 'date_of_birth', 'user_type']

Вопрос : Как вы думаете, это правильно? или я должен создать 1 класс с общими полями и 2 класса для разных типов пользователей?

Затем я хотел бы использовать свой собственный бэкэнд, поэтому я реализую файл backend.py в своем проекте.

backend.py

from fbLike.models import MyUser
from django.contrib.auth.backends import ModelBackend


class EmailAuthBackend(ModelBackend):
    def authenticate(self, email=None, password=None, **kwargs):
        try:
            user = MyUser.objects.get(email=email)
            if user.check_password(password):
                return user
        except MyUser.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return MyUser.objects.get(pk=user_id)
        except:
            return None

Для меня это кажется правильной реализацией.

Вопросы: Но как поступить с наследованием за несколькими таблицами? Является ли это возможным ? как узнать тип пользователя перед проверкой пароля, если у меня есть, например:

class BaseUser(AbstractBaseUser):
  email = models.EmailField(max_length=254, unique=True)
  # common fields


class Student(BaseUser):
  first_name = models.CharField(max_length=30)
  last_name = models.CharField(max_length=30)
  # ...


class employee(BaseUser):
  company_name = models.CharField(max_length=100)
  # ...

Наконец, с моими моделями.py, backend.py я попытался бы использовать систему CRUD в своих представлениях.py для создания и обновления пользователей.

Под моими файлами:

forms.py

class StudentProfileForm(forms.ModelForm):
    phone = forms.CharField(required=True)
    faculty = forms.ModelChoiceField(Faculty.objects.all())
    campus = forms.ModelChoiceField(Campus.objects.all())
    cursus = forms.ModelChoiceField(Cursus.objects.all())
    year = forms.IntegerField(required=True)

    class Meta:
        model = MyUser
        fields = ('email', 'password', 'first_name', 'last_name', 
                  'date_of_birth', 'phone', 'faculty', 'campus', 'cursus', 'year'
        )
        widgets = {
            'password': forms.PasswordInput(render_value=True),
        }

class EmployeeProfileForm(forms.ModelForm):
    date_of_birth = forms.DateField(widget = AdminDateWidget)
    phone = forms.CharField(required=True)
    faculty = forms.ModelChoiceField(Faculty.objects.all())
    campus = forms.ModelChoiceField(Campus.objects.all())
    job = forms.ModelChoiceField(Function.objects.all())
    desk = forms.IntegerField(required=True)

    class Meta:
        model = MyUser
        fields = ('email', 'password', 'first_name', 'last_name', 
                  'date_of_birth', 'phone', 'faculty', 'campus', 'job', 'desk'
        )
        widgets = {
            'password': forms.PasswordInput(render_value=True),
        }

Здесь я думаю, что это слишком сложно, но я не знаю, как уменьшить сложность.

views.py

class RegisterProfile(CreateView):
    model = MyUser
    template_name = 'fbLike/user_profile.html'
    form_class = StudentProfileForm
    second_form_class = EmployeeProfileForm

    def get_object(self):
        return super(RegisterProfile, self).get_object()

    def get_success_url(self):
        return reverse('login',)

    def get_context_data(self, **kwargs):
        context = super(RegisterProfile, self).get_context_data(**kwargs)
        if 'studenForm' not in context:
            context['studentForm'] = self.form_class
        if 'employeeForm' not in context:
            context['employeeForm'] = self.second_form_class
        return context


    def form_valid(self, form):
        self.object = MyUser.objects.create_user(**form)
        return super(RegisterProfile, self).form_valid(form)

    def form_invalid(self, form):
        return super(RegisterProfile, self).form_invalid(form)

    def post(self, request, *args, **kwargs):
        self.object = None
        if request.POST['profileType'] == 'student':
            form = self.form_class(request.POST, prefix='st')
            form.user_type = 'student'
        else:
            form = self.second_form_class(request.POST, prefix='em')
            form.user_type = 'employee'

        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)


class UpdateProfile(UpdateView):
    model = MyUser
    template_name = 'fbLike/modify_profile.html'

    def get_form_class(self):
        if self.request.user.user_type == 'student':
            return StudentProfileForm
        return EmployeeProfileForm

    def get_success_url(self):
        return reverse('welcome',)

Для CreateProfile я хотел бы отправить 2 формы в своем шаблоне, и благодаря javascript я могу выбрать правильную форму. Но когда я отправляю форму, у меня возникает ошибка с паролем пользователя. Когда я проверяю своих пользователей на странице администратора, кажется, что система hashpassword дает сбой. Итак, моя первая попытка решить: это было переопределить метод сохранения в моей форме, чтобы использовать request.user.set_password, но это не работает.

Я знаю, что это длинное объяснение. Но каждый может дать мне пример класса CreateView с 2 формами, пожалуйста? Если невозможно использовать класс, но функцию, как реализовать эту функцию?

Я благодарю вас заранее


person Vaatileplow    schedule 16.06.2014    source источник
comment
Прочтите это.   -  person Burhan Khalid    schedule 16.06.2014


Ответы (1)


Я постараюсь ответить на ваши многочисленные вопросы, но, пожалуйста, в будущем - по одному вопросу на пост. Я понимаю, что вы новичок, но StackOverflow не является дискуссионным форумом, загляните в раздел справки (ссылка внизу каждой страницы), в частности, в раздел задавать вопросы.


Я хочу создать несколько пользователей с аутентификацией по электронной почте. [...] Как вы думаете, это правильно? или я должен создать 1 класс с общими полями и 2 класса для разных типов пользователей?

Это правильно, если это работает с вами. Такие вопросы лучше подходят для codereview.stackexchange.com. Тем более, что бага нет.

Но как поступить с многотабличным наследованием? Является ли это возможным ? как узнать тип пользователя перед проверкой пароля, если у меня, например, [...]

Во-первых, механизмы аутентификации — это вообще отдельная тема; они не имеют ничего общего с множественным наследованием таблиц (я думаю, вы имеете в виду отношения или несколько профилей пользователей).

Вам нужно только несколько серверных частей аутентификации, если ваш алгоритм аутентификации пароля отличается для каждого типа пользователя. Если все пароли пользователей хранятся в одном месте/системе/базе данных, вам не нужны несколько механизмов аутентификации.

Вы должны реализовать метод в своем базовом пользовательском классе, который возвращает тип пользователя. Затем вы можете использовать user_passes_check декоратор для ограничения доступа к вашим представлениям.

Таким образом вы достигнете того, что я думаю, что вы после «этот раздел только для студентов» и т. д.

Наконец, с моими models.py, backend.py я бы попытался использовать систему CRUD в моем views.py для создания и обновления пользователей [..] Здесь я думаю, что это слишком сложно, но я не знаю, как уменьшить сложность .

Вам не нужно повторять все поля вашей модели в вашей ModelForm, вам нужно только переопределить поля, если вам нужно их изменить, или добавить в форму дополнительные «не модельные» поля. Вы можете начать с простой ModelForm, например:

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('name', 'email', 'password')

Обычно добавление дополнительного поля используется для запроса подтверждения пароля с помощью дополнительного поля пароля.

Для CreateProfile я хотел бы отправить 2 формы в своем шаблоне, и благодаря javascript я могу выбрать правильную форму. Но когда я отправляю форму, у меня возникает ошибка с паролем пользователя. Когда я проверяю своих пользователей на странице администратора, кажется, что система hashpassword дает сбой. Итак, моя первая попытка решить: это было переопределить метод сохранения в моей форме, чтобы использовать request.user.set_password, но это не работает.

Используйте мастер форм, чтобы реализовать мульти- пошаговый процесс регистрации. На первом этапе вы запрашиваете основную информацию и тип учетной записи; на втором этапе вы отправляете только соответствующую форму вместо отправки двух форм, а затем переключаете их с помощью javascript.

Я не вижу нигде в вашем коде, где вы используете set_password, поэтому не могу комментировать вторую часть.

person Burhan Khalid    schedule 16.06.2014