django auth User truncating email field - python

I have an issue with the django.contrib.auth User model where the email max_length is 75.
I am receiving email addresses that are longer than 75 characters from the facebook api, and I need to (would really like to) store them in the user for continuity among users that are from facebook connect and others.
I am able to solve the problem of "Data truncated for column 'email' at row 1" by manually going editing the field in our mySql database, but is there a better way to solve this? preferably one that does not involve me manually editing the database every time I reset it for a schema change?
I am ok with editing the database as long as I can add it to the reset script, or the initial_data.json file.

EmailField 75 chars length is hardcoded in django. You can fix this like that:
from django.db.models.fields import EmailField
def email_field_init(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 200)
CharField.__init__(self, *args, **kwargs)
EmailField.__init__ = email_field_init
but this will change ALL EmailField fields lengths, so you could also try:
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from django.db import models
User.email = models.EmailField(_('e-mail address'), blank=True, max_length=200)
both ways it'd be best to put this code in init of any module BEFORE django.contrib.auth in your INSTALLED_APPS
Since Django 1.5 you can use your own custom model based on AbstractUser model, therefore you can use your own fields & lengths.
In your models:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
email = models.EmailField(_('e-mail address'), blank=True, max_length=200)
In settings:
AUTH_USER_MODEL = 'your_app.models.User'

There is now a ticket to increase the length of the email field in Django: http://code.djangoproject.com/ticket/11579

Related

Is it better to make custom registration with editing Django UserCreationForm class or make it on a new table and class?

I wanted to make a Django authentication but I was confused about wichone is better?
is it is better to edit the venv/Lib/site-packages/django/contrib/auth/forms.py file and make my custom form and edit DB on venv/Lib/site-packages/django/contrib/auth/models.py or it's better to make my authentication system with model and views and forms on my application?
You don't need to add django's files. you can inherit there properties.
for example let's say you want to use User model of Django. you can do that in your models.py like following. here you ll get all the fields of Abstract User like first_name, email, passwords etc but you can add your own fields such as technoking in below example
from django.db import models
from django.contrib.auth.models import AbstractUser
import uuid
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
technoking = models.BooleanField(default=False)
objects = UserManager()
def __str__(self):
return self.username

Trying to add custom fields to users in Django

I am fairly new to django and was it was going well until this.
i'm trying to add fields 'address' and 'phone_number' to users (create custom user basically) but i keep getting this error like this...
File "C:\Users\will5\Desktop\City\city_info\forms.py", line 5, in <module>
class UserForm(forms.ModelForm):
File "C:\Users\will5\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\forms\models.py", line 257, in __new__
raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) (phone_number, address)
specified for User
this is models
...
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone_number = models.IntegerField(default=0)
address = models.CharField(max_length=150)
...
this is in my forms
from django.contrib.auth.models import User
from django import forms
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username', 'email', 'password', 'address', 'phone_number']
I also read that in order to fix this problem i will have to restart my project by creating the custom user and migrating and i don't wanna do that.
The problem is that you are still importing the built in user model
You can fix it by replacing
from django.contrib.auth.models import User
with
from django.contrib.auth import get_user_model
User = get_user_model()
This assumes that you have set AUTH_USER_MODEL = 'yourapp.User' in your settings.
As you have read in the docs, it is extremely difficult to switch to a custom user model after the project has started, there isn't an easy solution to this. If you can't restart your project, perhaps you could create a Profile model with a OneToOneField to the default User instead.

In Django, is it possible for superusers to have different required fields than non-superusers?

I know that superusers and regular users are both just django's User objects, but how can I write a custom user class that requires some fields for plain users and doesn't require those fields for superusers?
No structure in the database is tricky. JSONFields for example may prove to be extremely hard to tame when the app grows.
I would go and try to make it "simple" - more maintainable (I imagine if you need to do stuff like that you may want to extend the model in the future). If this is a new project you can easily change the default user model. But that may or may not help you with your case.
You can always make two models:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class Mortal(AbstractBaseUser):
is_superuser = False
username = models.CharField(max_length=256)
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
class Admin(AbstractBaseUser):
is_superuser = True
username = models.CharField(max_length=256)
and then make your own authentication backend:
class MyBackend(object):
"""
Danger! A backend to authenticate only via username
"""
def authenticate(self, username=None):
try:
return Mortal.objects.get(username=username)
except Mortal.DoesNotExist:
try:
return Admin.objects.get(username=username)
except Admin.DoesNotExist:
return None
You can have a profile class (say UserProfile) with foreign key to the user that is to be created only when user signs up using the website's registration form. That way, superuser which is created on admin site or through command line wouldn't need an extra profile instance attached to it.

Django 1.7 multisite User model

I want to serve a Django application that serves multiple web sites by single database but different user sets. Think like a blog application, it will be used by several domains with different themes, but use same database by adding a site field to models.
I use Django's SitesFramework for that job. But the problem is, I couldn't separate user models for different sites. I want to use same user model with a site field and email field that unique per site.
I tried to extend AbstractUser model like that:
from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Member(AbstractUser):
username = None
site = models.ForeignKey(Site)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
on_site = CurrentSiteManager()
class Meta:
unique_together = ('site', 'email')
But gives that error: 'Member.email' must be unique because it is named as the 'USERNAME_FIELD'.
What is the best practice for that issue?
I hope this approach helps to you:
1) Compose username before save:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Member(AbstractUser):
site = models.ForeignKey(Site)
on_site = CurrentSiteManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
class Meta:
unique_together = ('site', 'email')
from django.db.models.signals import pre_save
from django.dispatch import receiver
#receiver(pre_save, sender=Member)
def compose_username(sender, instance, **kwargs):
instance.username = "{0}__{1}".format( instance.email, instance.site_id )
2) Then overwrite ModelBackend in your custom auth backend:
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
class MyModelBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
site = kwargs.get('site')
identifier = "{0}__{1}".format( username, site )
try:
user = UserModel.objects.get(username=identifier)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
3) Remember set your custom backend on settings:
AUTH_USER_MODEL='s1.Member'
SITE_ID = 1
AUTHENTICATION_BACKENDS = ( 'MyApp.MyModule.MyModelBackend',)
4) Include site when authenticate:
>>> from s1.models import Member as M
>>> m1 = M()
>>> m1.site_id = 1
>>> m1.email = 'peter#hello.com'
>>> m1.save()
>>> m1.set_password('hi')
>>> m1.save()
>>>
>>> from django.contrib.auth import authenticate, login
>>> u=authenticate(username='peter#hello.com', password='hi', site=1)
>>> u
<Member: peter#hello.com_at_1>
>>>
Well if you want to keep the email as the USERNAME_FIELD, which by definition in the User-model must be always unique, you won't be able to repeat it for each site.
There are more than one approaches I can think of that would probably work, but I guess I would do the following:
First of all, I wouldn't extend the AbstractUser-model and make a
OneToOne dependency to the Site. Because a User is actually allowed to belong
to more than one site. So here, the best option imo is to create a
Member-model with a ForeignKey to User and a Site field and make
those unique_together. So there is only one Member per Site, and a User remains unique. Which is what represents the case better in real life too.
Now, when registering a new user for a site, just make a check first if the User (email-Address) already exists, and if so, just assign a new Member to that User. If not, create a new User as well.
First Edit to the question, "what if a User want's to register to another site with different username, password or email?"
If according to my comments, it's OK to share a user account for the sites (and of course the user is aware of this) In the register-process, in the case the user already exists for a given email, then he could be informed that, as an account for that address already exists for the site-a, this user account would be assigned to the membership to site-b. Then, an e-mail with a verify link could be sent, and when confirmed, the new member would be created and assigned to the valid user.
Another approach
If I was wrong assuming, it's ok and even desired to share users among sites, then I guess a whole new approach is needed here:
Extend the AbstractUser as you were doing, but instead of using the email as USERNAME_FIELD, use a new field composed from <email>_<site_id> (which would always be unique, as these 2 fields are unique_together)... the field could be called unique_site_id or so. And this field could be populated after submitting the sign-in and login forms.

How to extend the django User model?

I'm trying to implement the Users class from django.contrib.auth.models like this:
from django.db import models
from django.contrib.auth.models import User
class Registration(models.Model):
'''Represents a user registration.'''
user = models.ForeignKey(User)
registration_date = models.DateTimeField(auto_now_add=True, help_text='The date of the registration')
def __str__(self):
return '%s - %s' % (self.user, self.registration_date,)
This user have two attributes enabled by default: username, password
Reading at the code I can see that there are more attributes, like name and email.
How can I enable those hidden (if this is correct) attributes?
First, these attributes are not hidden. Assuming you have "django.contrib.auth" and "django.contrib.contenttypes" in your INSTALLED_APPS, then you have access to the User model as it is defined in your link. See here for the documentation on how to use/access it.
However, since you specified extending the User model, I expect you wanted to add some of your own fields to it (even though your example registration_date exists and is accessible via myuser.date_joined).
The older, more stable and more common way of doing this is similar to what you have. The only difference is to use a OneToOneField(User) instead of a ForeignKey(User). This makes the relationship bidirectional and more convenient by forcing one. You do need to make sure and create a Registration object for every User created.
In fact, there is an example of exactly what you want in the docs for the OneToOneField.
from django.db import models
from django.contrib.auth.models import User
class Registration(models.Model):
user = models.OneToOneField(User)
registration_date = models.DateTimeField(auto_now_add=True)
>>> user = User.objects.get(pk=1)
>>> registration = Registration.objects.create(user=user)
>>> user.registration.registration_date
# Should give the current time
>>> user.get_full_name()
# Should also have all those *hidden* attributes linked above
As of Django 1.5, you can use your own custom User model fairly easily. The documentation for this feature is here. If you are just adding some extra fields, then you can subclass the User model and add your own fields.
from django.db import models
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
# We inherit all those nifty things from AbstractUser
registration_date = models.DateTimeField(auto_now_add=True)
Then enable it in your settings.py by adding AUTH_USER_MODEL = 'myapp.MyUser'. We now have to access the user model a little differently
>>> from django.contrib.auth import get_user_model()
>>> Users = get_user_model()
>>> user = Users.objects.get(pk=1)
>>> user.registration_date
# Should give the current time
>>> user.get_full_name()
# Should have those 'hidden' attributes
All this is available under extending and substituting the User model in the documentation.

Categories