The relation between a model and it's form in Django, and user authentication - django

I'm migrating something from an old PHP/apache server to Django. I'm a bit stumped with the 'ModelForm'.
As far as I understand, a "Model" is the abstraction for persistent elements in my website/server - specifically this is something stored physically, say in a database, and defines the fields (read columns) in the DB.
I started moving the authentication part of the site, and discovered models, and specifically the User model (I made an empty User inheriting AbstractUser just in case I will ever need to extend things). Now I want to create a simple two field form, to authenticate login.
The form:
Username (which is a field of User, by default)
Password (Which is not).
Even the 'Username' needs a redefinition in the model form. So my questions:
What is the advantage of the model form (over just a form)? - seems like you're redefining fields anyway, and obviously sometimes adding fields on top of the model.
Specifically for authentication, I probably need to store my salted hash associated with the user somehow, compare my password using that and retrieve the user object. This is something I find very hard to find in the Django docs - they just have too much written on authentication, and not one full code example. Do I put this in the "validate" method of form, retrieving there an object and storing it in a session or something?
If there is a deeper relation between a model form and the associated model, I would like to know as well.

Simple django forms and modelforms have quite differences.
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
The above example illustrates that you don't have to write any form field in here. The model form will itself create a form which is based on the attributes provided in the model ('Article' in this example).
If you create a simple django form then it would be something like:
class ArticleForm(forms.Form):
some_field = forms.CharField(some_attrs)
...
The django User model provides you everything you need for authentication. When you want to create users just import django.contrib.auth.models.User and use create method to create objects. Then when you want to authenticate a user use authenticate method.
from django.contrib.auth import authenticate, login
def user_login(request):
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
# after authentication login the user or set cookies or modify the session or some other action can be taken
return HttpResponse("Some response or use render for html page")
username and password will be coming from your post request.
If you want to extend default Django user model you can use django user model as onetoonefield in your extended model.
class AppUser(models.Model):
user = models.OneToOneField(User)
... # other custom fields

Related

Forcing unique email address during registration with Django

Is there a simple way to force unique email address's during registration with website built with Django?
I've seen some "addons?" like HMAC, but it seems a bit too complicated for what I am trying to achieve.
Also, would it be possible to accept registration only from a list of domains? (such as only emails from "#google.com")
I had the same problem and solved it by extending the AbstractUser class to my own class MyUser and changing the defaults.
Then by making the this class MyUser as a default model class for all my users I could apply this property(unique E-Mail) to all my users on my web app.
Create an app myuser. There in models.py:
from django.contrib.auth.models import AbstractUser
#create your own user class.
class MyUser(AbstractUser):
def __init__(self, *args, **kwargs):
self._meta.get_field('email').blank = False
self._meta.get_field('email')._unique = True
super(MyUser, self).__init__(*args, **kwargs)
#Changed the defaults above.
#Give any additional field you want to associate your user with.
NOTE: AbstractUser already has all the basic fields you would want a User Model to have. For example: username, password, email etc. Check all of them here.
The last thing you would want to do is add the following in your setting.py
AUTH_USER_MODEL = 'myuser.MyUser'
This will make sure that the default user is associated with your web app is the extended(modified) MyUser class. This will provide you with all the basic functionalities that django provides for a User.
login
logout
in your views: you can get user instance in: request.user
etc.
I would like to suggest that you may need some additional code(in forms.py and views.py) to create a user through this type of class. I hope you will manage that. This should be enough to guide you in the right direction.
Maybe a library would have helped but since you needed an authentication for emails' domains as well, I think this should do the trick. In my humble opinion, you can't always depend on the 3rd party libraries for every other functionality.
Lastly, as you asked to authenticate a user coming only from a domain like #gmail.com or #outlook.com, a simple check in your django forms' clean method would do the trick. I hope you know how to handle django forms. If not, then you can learn about them in the official docs. They are an essential part of Django.
You can check the E-Mail with this logic:
email = self.cleaned_data['email']
email_source = email.split('#')[-1]
#email_source will now have values like: gmail.com, outlook.com etc
#you can now validate email_source now like:
permitted_sources = ['gmail.com' , 'outlook.com' , ]
if email_source in permitted_sources:
return cleaned_data
else:
raise forms.ValidationError('Error Message')
#Note: This logic should be kept in your clean method.
I hope this guides you. Thanks.

Why Django does not check email integrity when creating a new user?

I don't understand this behaviour. Let's say I open a Django shell and type:
from django.contrib.auth.models import User
user = User.objects.create(username="toto", email="titi")
Why does Django let me create this user (with an invalid email) without raising an error?
I have the same "no verification behaviour" creating a user in a POST in my API created with tastypie.
The question is:
As Django does not seem to check this by itself, where am I supposed to perform this kind of verifications sothat I don't have to write them several times (since a user can be created in several ways like website, API, etc.)?
Thanks.
Django doesn't implicitly do any validation if you just call .create() or .save() - you need to explicitly use model validation, or save the object via a
ModelForm. Your example with model validation would look like this:
user = User(username="toto", email="titi")
try:
user.full_clean()
except ValidationError as e:
# handle the error...
pass
user.save()
Or using a ModelForm:
class UserForm(forms.ModelForm):
class Meta:
model = User
f = UserForm(dict(username="toto", email="titi"))
if f.is_valid():
user = f.save()
else:
# handle error, ...
pass
Both model validation and ModelForms invoke the model field's validators, so in the case of the User's email, no additional work is needed for validation. If you need to do custom validation, you can do this in the ModelForm class - it is common to have a forms.py file in the app as a central place for Forms and ModelForms.
The same goes for Tastypie - the default configuration assumes the data submitted is valid. You can override this with the FormValidation class, which uses a Django Form or ModelForm for its validation. A full example would look something like this:
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
validation = FormValidation(form_class=UserForm) # UserForm from above
# more options here....

Django Social Auth store username from Facebook in Custom User Model

I've just integrated Django-Social-Auth (DSA) v0.7.23 using django 1.5 with a custom User model and auth/login is working fine except for the username field which is not being stored in my custom User model.
Stepping through the DSA code it appears to explicitly remove the username which was passed back by facebook. Here is the function in question that pops the username off:
#classmethod
def username_field(cls, values):
user_model = cls.user_model()
if hasattr(user_model, 'USERNAME_FIELD'):
# Django 1.5 custom user model, 'username' is just for internal
# use, doesn't imply that the model should have an username field
values[user_model.USERNAME_FIELD] = values.pop('username') # Username removed
return values
How can I go about getting the username that was passed back from facebook and not having it be explicitly removed by DSA?
I believe a work around would be to create a custom pipeline that generates a username. However I was wondering if anyone else encountered this scenario before and leveraged anything that already exists within DSA (i.e. a particular settings.py configuration)
Thanks.
The original username is available in the details attribute passed to pipeline functions, take this for example:
def generated_username(user, details, *args, **kwargs):
username = details['username']
user.your_field = username
user.save()
It's worth noting that the username is popped from values if you have a USERNAME_FIELD defined in your custom model.

Django authentication with custom model User

For my needs builtin model User is not enough... So I have my own model UserProfile and I want make authentication on site through this model (UserProfile does not inherit from User model and not related to it at all).
My Model:
class UserProfile(models.Model):
password = models.CharField(max_length = 40)
email = models.EmailField(max_length = 72, unique = True)
## Add this so that you can use request.user.is_authenticated
def is_authenticated(self):
return True
But builtin authentication uses model User.
So I want to understand how can I change that, so authentication use my model UserProfile with all auth features???
A good tutorial would be great!
(Step by step in views, models and authentication)
PS: I know I can store extra data in other model but I don't want that
Here's an even more extreme example but illustrates that what you want to do can be done. The author not only replaces the User model which the authentication backend uses but also uses SQLAlchemy instead of the Django ORM. http://tomforb.es/using-a-custom-sqlalchemy-users-model-with-django
The main point is that you need to write your backend authenticate and get_user methods to retrieve your custom User model. If you want to also support permissions you would need to write has_perm.
I used this article and it worked good enough for me, hope it can be useful for you.
Sultan

Anyone think django's user model is too tightly coupled with auth?

I'm trying to learn Django and I would like feedback from anyone who has any MVC/MTV/PHP/Ruby framework experience. Does anyone find that the user model is too tightly coupled with auth?
Background: When you first implement authentication for Django, you include the module django.contrib.auth
This will bring in several models like User, Group, Message etc. Let's focus on the User model as this is the one of the most important tables in any website.
In short the User table has these fields
User
username max_length 30, unique, [letters, digits, underscores]
password max_length 75
email max_length 75
...and about 8 other useful fields like first_name, last_name, etc.
Goal:
I want to remove username and use email as the login for every user. It's a pretty simple request that many websites use these days.
I don't want to monkey patch the core code since this will make upgrading more difficult later on. This means modifying the User model is out of the question. I only want to do a few simple and basic things I expect a few frameworks to do so let me address how Django does it.
Adding new fields to the User model
Django docs says to use create another table and insert the fields there. You will have a one to one relationship between the User table and the Profile table.
eg.
If You want to add an image field to each user you add it to the profile table. A join query is made every single time. They've even specified a constant to tell the framework what table to use:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
I don't think it's the best practice to have to do a join query every time I want a field that should belong to the user table.
Another option is to use the function add_to_class.
The django community has stated it's not good to define new fields outside of the main class because other developers who add methods won't know all the data members.
Editing old fields
The auth module does a check against two fields username and the hashed password. Looking at the above table I would need to change the username model to accept these properties. Length of 75 with all the valid characters of the email. The django suggests I check against the email field.
Two problems arise if I use the email field to auth against:
I need to write a new class to be used in a constant AUTHENTICATION_BACKEND, so it checks against the email field and I have an unused field called username.
Adding new methods
In MVC/MTV a design principle is to use fat models skinny controllers. Since the model is declared in auth, I'm not sure how one is supposed to add methods that act on the user model's fields. Since django suggests using a Profile model, I suppose they will have to go there.
Extending the User class
A small annoyance would be that I can't use the name 'User' and instead must use 'Users' or 'Accounts'. A bigger one is I don't think the auth would recognize this new module. Meaning I would have to rewrite a bunch functionality that is is present. This one doesn't bother me as it's something I expect to do in other frameworks.
Any comments are appreciated. I wouldn't ask all these questions and look for solutions if I wasn't truly interested in using django.
I agree that django's incessant clinginess to the auth models is absurd. My job requires me to create ultra scalable and very high load sites which sometimes require user authentication and djano's auth model + permissions does not fit with that.
Fortunately, it's not difficult to replace.
First, create a custom User model.
class User(models.Model):
...fields...
#Define some interface methods to be compatible.
def get_and_delete_messages(self):
def is_active(self):
def is_anonymous(self):
def is_authenticated(self):
def is_staff(self):
def has_perm(self, perm_list):
Second, create your own authentication back-end.
class LocalAccount(object):
"""
This checks our local user DB for authentication
"""
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(alias=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.select_related().get(pk=user_id)
except User.DoesNotExist:
return None
#settings.py
AUTHENTICATION_BACKENDS = (
'helpers.auth.LocalAccount',
)
That should solve most of your issues, I don't even think all of the methods you would find on django.contrib.auth.User are necessary, I recommend trying it out.
The one gotcha here is that the admin may start to bitch, fortunately that's really easy to patch using simple python inheritance as well. That's another question though :)
At the end of the day your project's auth backend needs some sort of store for auth credentials. That the default auth backend is tightly coupled to the User model is not strange in this respect. It's easy enough to substitute your own definition for the user model if you write your own auth backend, as I have in the past.
I created my Profile model and use AUTH_PROFILE_MODULE, so I have complete control over my model, I can modify fields, add methods, etc. Now I'm thinking about using cache and writing middleware that will get profile from cache if possible.
To login using email you could write very simple auth backend:
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
class EmailModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None