0

I am trying to create customuser for my Django project with email as username and add a radio button for truck and company. so that in the registration process email-id will be registered as per truck or company. I mentioned the radio button as 'Tag' and add a ManytoMany field to Tag in EmailUser model. When i do makemigrations, it was raising an error : (fields.E300) Field defines a relation with model 'AbstractEmailUser' which is either not installed, or is abstract.

I am quite new to Django and not sure whether I have created a correct code for what I really wants. Please help me solving this. here is my code,

models.py:

import django
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin)
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _



class EmailUserManager(BaseUserManager):

"""Custom manager for EmailUser."""

def _create_user(self, email, password,
                 is_staff, is_superuser, **extra_fields):
    """Create and save an EmailUser with the given email and password.

    :param str email: user email
    :param str password: user password
    :param bool is_staff: whether user staff or not
    :param bool is_superuser: whether user admin or not
    :return custom_user.models.EmailUser user: user
    :raise ValueError: email is not set

    """
    now = timezone.now()
    if not email:
        raise ValueError('The given email must be set')
    email = self.normalize_email(email)
    is_active = extra_fields.pop("is_active", True)
    user = self.model(email=email, is_staff=is_staff, is_active=is_active,
                      is_superuser=is_superuser, last_login=now,
                      date_joined=now, **extra_fields)
    user.set_password(password)
    user.save(using=self._db)
    return user

def create_user(self, email, password=None, **extra_fields):
    """Create and save an EmailUser with the given email and password.

    :param str email: user email
    :param str password: user password
    :return custom_user.models.EmailUser user: regular user

    """
    is_staff = extra_fields.pop("is_staff", False)
    return self._create_user(email, password, is_staff, False,
                             **extra_fields)

def create_superuser(self, email, password, **extra_fields):
    """Create and save an EmailUser with the given email and password.

    :param str email: user email
    :param str password: user password
    :return custom_user.models.EmailUser user: admin user

    """
    return self._create_user(email, password, True, True,
                             **extra_fields)


class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):

"""Abstract User with the same behaviour as Django's default User.

AbstractEmailUser does not have username field. Uses email as the
USERNAME_FIELD for authentication.

Use this if you need to extend EmailUser.

Inherits from both the AbstractBaseUser and PermissionMixin.

The following attributes are inherited from the superclasses:
    * password
    * last_login
    * is_superuser

"""

email = models.EmailField(_('email address'), max_length=255,
                          unique=True, db_index=True)

is_staff = models.BooleanField(
    _('staff status'), default=False, help_text=_(
        'Designates whether the 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 = EmailUserManager()

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []

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

# def __init__(self, *args, **kwargs):
    # super(AbstractEmailUser, self).__init__(*args, **kwargs)
    # if self.instance.pk:
            # self.fields['Tag'].initial = self.instance.Tag_set.all()

def get_full_name(self):
    """Return the email."""
    return self.email

def get_short_name(self):
    """Return the email."""
    return self.email

def email_user(self, subject, message, from_email=None, **kwargs):
    """Send an email to this User."""
    send_mail(subject, message, from_email, [self.email], **kwargs)


# Monkey patch Django 1.7 to avoid detecting migrations
if django.VERSION[:2] == (1, 7):
last_login = AbstractEmailUser._meta.get_field('last_login')
last_login.blank = True
last_login.null = True
last_login.default = models.fields.NOT_PROVIDED
groups = AbstractEmailUser._meta.get_field('groups')
groups.help_text = _('The groups this user belongs to. A user will get '
                     'all permissions granted to each of their groups.')


class EmailUser(AbstractEmailUser):

"""
Concrete class of AbstractEmailUser.

Use this if you don't need to extend EmailUser.

"""
CHOICES = (('Truck', 'Truck'),('Company', 'Company'),)
Tag = models.ManyToManyField(AbstractEmailUser)
class Meta(AbstractEmailUser.Meta):
    swappable = 'AUTH_USER_MODEL'

forms.py:

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.utils.translation import ugettext_lazy as _


class EmailUserCreationForm(forms.ModelForm):

"""A form for creating new users.

Includes all the required fields, plus a repeated password.

"""

error_messages = {
    'duplicate_email': _("A user with that email already exists."),
    'password_mismatch': _("The two password fields didn't match."),
}

password1 = forms.CharField(
    label=_("Password"),
    widget=forms.PasswordInput)
password2 = forms.CharField(
    label=_("Password confirmation"),
    widget=forms.PasswordInput,
    help_text=_("Enter the same password as above, for verification."))
CHOICES= (('Truck', 'Truck'),('Company', 'Company'),)
Tag = forms.ChoiceField(choices=CHOICES, label='Tag', widget=forms.RadioSelect())

class Meta:
    model = get_user_model()
    fields = ('email', 'Tag',)

def clean_email(self):
    """Clean form email.

    :return str email: cleaned email
    :raise forms.ValidationError: Email is duplicated

    """
    # Since EmailUser.email is unique, this check is redundant,
    # but it sets a nicer error message than the ORM. See #13147.
    email = self.cleaned_data["email"]
    try:
        get_user_model()._default_manager.get(email=email)
    except get_user_model().DoesNotExist:
        return email
    raise forms.ValidationError(
        self.error_messages['duplicate_email'],
        code='duplicate_email',
    )

def clean_password2(self):
    """Check that the two password entries match.

    :return str password2: cleaned password2
    :raise forms.ValidationError: password2 != password1

    """
    password1 = self.cleaned_data.get("password1")
    password2 = self.cleaned_data.get("password2")
    if password1 and password2 and password1 != password2:
        raise forms.ValidationError(
            self.error_messages['password_mismatch'],
            code='password_mismatch',
        )
    return password2

def save(self, commit=True):
    """Save user.

    Save the provided password in hashed format.

    :return custom_user.models.EmailUser: user

    """
    user = super(EmailUserCreationForm, self).save(commit=False)
    user.set_password(self.cleaned_data["password1"])
    if commit:
        user.save()
    return user


class EmailUserChangeForm(forms.ModelForm):

"""A form for updating users.

Includes all the fields on the user, but replaces the password field
with admin's password hash display field.

"""

password = ReadOnlyPasswordHashField(label=_("Password"), help_text=_(
    "Raw passwords are not stored, so there is no way to see "
    "this user's password, but you can change the password "
    "using <a href=\"password/\">this form</a>."))

class Meta:
    model = get_user_model()
    exclude = ()

def __init__(self, *args, **kwargs):
    """Init the form."""
    super(EmailUserChangeForm, self).__init__(*args, **kwargs)
    f = self.fields.get('user_permissions', None)
    if f is not None:
        f.queryset = f.queryset.select_related('content_type')

def clean_password(self):
    """Clean password.

    Regardless of what the user provides, return the initial value.
    This is done here, rather than on the field, because the
    field does not have access to the initial value.

    :return str password:

    """
    return self.initial["password"]

admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext_lazy as _

from .forms import EmailUserChangeForm, EmailUserCreationForm
from .models import EmailUser


class EmailUserAdmin(UserAdmin):

"""EmailUser Admin model."""

fieldsets = (
    (None, {'fields': ('email', 'password', 'Tag')}),
    (_('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', 'Tag')
    }
),
)

# The forms to add and change user instances
form = EmailUserChangeForm
add_form = EmailUserCreationForm

# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'is_staff', )
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups', 'Tag')
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions', 'Tag',)

# Register the new EmailUserAdmin
admin.site.register(EmailUser, EmailUserAdmin)
vanam
  • 135
  • 1
  • 4
  • 14
  • You have a choice field in your form with `CHOICES= (('Truck', 'Truck'),('Company', 'Company'),)`, but you have a many-to-many field in your model, which is something completely different. What are you trying to do here? – Alasdair Jul 04 '16 at 14:04
  • Initially I have written Tag = models.CharField(choices=CHOICES, max_length=20). But when i do migrations it raising an error, filter_horizontal in your admin.py must be a many-to-many field and hence i changed it manytomany field. – vanam Jul 04 '16 at 14:09
  • The [`filter_horizontal`](https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#django.contrib.admin.ModelAdmin.filter_horizontal) option is only meant for many-to-many fields. If your data is a `CharField`, you should stick with a `CharField`, you shouldn't change it to a many-to-many field just to use `filter_horizontal`. – Alasdair Jul 04 '16 at 14:12
  • So, how can get rid out of the error raised to make manytomany fields for filter_horizontal which consists of ('groups', 'user_permissions', 'Tag',). – vanam Jul 04 '16 at 14:17
  • If `Tag` isn't a many-to-many field, then it shouldn't be in `filter_horizontal`. So you should have `filter_horizontal = ('groups', 'user_permissions')`. – Alasdair Jul 04 '16 at 14:20
  • Hi, I am in a similar situation: here: (http://stackoverflow.com/questions/39566144/correct-way-to-extend-abstractuser-in-django). Did you manage to figure it out? Would you be able to take a look at my situation too? – Dr Confuse Sep 19 '16 at 06:04

0 Answers0