I create registration form:
#urls.py
from django.conf.urls import patterns, url
from django.views.generic import TemplateView
from account.views import Register
urlpatterns = patterns('',
url(r'^register/$', Register.as_view(template_name='account/register.html')),
)
#views.py
from django.views.generic import CreateView
from django.contrib.auth.models import User
class Register(CreateView):
model = User
success_url = '/account/'
And i have question: how I can add that email be require (now I must only enter username, password and 2 times time).
#edit
And how "repair" password? When i create user (in this form) and then go to admin panel, in user i see "Invalid password format or unknown hashing algorithm.". How i can repair this?
The reason that email is not required is because you're using a ModelForm, which takes a lot of cues from the underlying User model. Specifically, the required=True attribute is not present on the email field of the model.
One solution is to create your own form with the necessary attributes, perhaps by using a ModelForm and adding a required email field.
Another solution, and probably the better one, is to use something like django-registration as mentioned by Aamir Adnan in the comments to your question. It'll simplify things a lot for you.
As far as your repair password goes, you can't set the password to a raw string value as you're doing with your CreateView. To set a password, you have to call user.set_password(raw_string) which will take care of hashing and salting for you. Look how the built in UserCreationForm works, and try to mimic it if you decide to build the form yourself, rather than using a library (you shouldn't).
To solve these two problems you can use form like:
class UserCreationForm(forms.ModelForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(UserCreationForm).__init__(*args, **kwargs)
self.fields['email'].required = True
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
class Register(CreateView):
model = User
form_class = UserCreationForm
success_url = '/account/'
But there'll be other problems like duplication of username.
Related
hi everyone i'm trying to make a blog and i want to associate a user to post .. and to comment , it's a multi-user blog and i don't know how to do it , any help guys ? !
here is the model file :
from django.db import models
from django.utils import timezone
from django.conf import settings
from django.utils.text import slugify
# Create your models here.
#this is for categories
class Category(models.Model):
title=models.CharField(max_length=100,default='')
def __str__(self):
return self.title
#this is where a user can create his own gigs
class Gigposter(models.Model):
title=models.CharField(default='',max_length=100,blank=False)
user=models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=False)
categories=models.OneToOneField(Category,on_delete=models.PROTECT,default='',null=False)
published_at=models.DateTimeField(auto_now_add=True)
description=models.TextField(default='',max_length=None,blank=False)
mainphoto=models.ImageField(default='')
photo=models.ImageField()
def __str__(self):
return self.title
#this is where a user can comment and say what he thinks about others work
class Comment_area(models.Model):
user=models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=False)
comment=models.TextField(max_length=None,default='')
commented_at=models.DateTimeField(auto_now_add=True)
and the views file is empty as you can see :
from django.shortcuts import render
# Create your views here.
Wouldn't recommend using a OneToOneField here, as it tells Django the user is tied to exactly one comment/post (when, a user is likely to post more than once).
You could use models.ForeignKey:
from django.contrib.auth.models import User
class Gigposter(models.Model):
# Other properties...
user = models.ForeignKey(
'User'
on_delete=models.CASCADE
)
If you want to automatically associate a user to a post when working on the admin page, you should redefine save_model method of your model. This method describes everything what should be done when you save your model. In your case you should add something like
class GigposterAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = `request.user`
super().save_model(request, obj, form, change)
admin.site.register(Gigposter, GigposterAdmin)
to your admin.py. You should also exclude the user field from fieldset in GigposterAdmin. See this for reference.
If you need to identify user in your views, you can always use request.user. In particular, you can pass it as a part of the context for generating a view. Hope this helps.
I am relatively new to Django, and web development in general. I'm trying to build a website with two types of users, customers, and suppliers. I need to be able to show these two types of customers different things on the website. For example, different links in the header section for the suppliers.
I am under the impression the best way to do this is to create two user groups ('suppliers' and 'customers') in my /admin, create two different sign up forms (one for suppliers and one for customers), and send each to their respective user group on sign up form submission. From there I can decide what the user sees based on their user group. Correct?
Unfortunately, I'm nearly at my wits end with this! I've created the user groups, created the different sign-up forms, but for the last 2 days I have been trying to figure out how to send the signups to their respective group and I just can't manage to do it! I've searched high and low and tried every suggested line of code I could find: no luck.
Most of the stuff I have tried is along these lines:
views.py
from __future__ import unicode_literals
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.contrib.auth.models import User, Group
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, render, get_object_or_404
from .forms import SupplierRegisterForm
def supplier_signup_view(request):
form = SupplierRegisterForm(request.POST or None)
if form.is_valid():
user = form.save(commit=False)
user.groups.add(Group.objects.get(name='suppliers'))
password = form.cleaned_data.get('password')
user.set_password(password)
user.save()
new_user = authenticate(username=user.username, password=password)
login(request, new_user)
if next:
return redirect(next)
return redirect("/")
context = {
"form": form,
"title": title,
}
return render (request, "supplier_signup.html", context)
forms.py
from django import forms
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.contrib.auth.models import User, Group
User = get_user_model()
class SupplierRegisterForm(forms.ModelForm):
username = forms.CharField()
email = forms.EmailField(label="Email Address")
email2 = forms.EmailField(label="Confirm Email", widget=forms.TextInput(attrs={'autocomplete':'false'}))
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = [
'username',
'email',
'email2',
'password',
]
def clean_email2(self):
email = self.cleaned_data.get('email')
email2 = self.cleaned_data.get('email2')
if email != email2:
raise forms.ValidationError("Emails must match")
email_qs = User.objects.filter(email=email)
if email_qs.exists():
raise forms.ValidationError("This email has already been registered")
return email
It would be much appreciated if anyone could give me a beginners walkthrough of how I would go about automatically sending users to a user group on form submission. Please include all of the code I would need to use, whether in settings, urls.py, models.py, forms.py, views.py, or templates (including imports etc), and any commands I would need to perform and when (eg migrate).
Many thanks!
EDIT: I've changed the answer to make use of Django's API.
Call user.save_m2m() after user.save():
if form.is_valid():
...
user.save()
user.save_m2m()
...
Explanation (from Django docs):
If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn’t possible to save many-to-many data for an instance until the instance exists in the database.
To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass. After you've manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.
I've read through several similar questions here, but I still can't seem to find a solution:
I am using the django-registration package and I want to make a registration form that does not have an email field, or at least not required. I have tried many different ways, but here is one:
# in forms.py
from registration.forms import RegistrationForm
class ExpRegistrationForm(RegistrationForm):
# email = forms.EmailField(label="E-mail",required=False)
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['email'].required = False
Then I link this to a custom view:
# in views.py
from registration.backends.simple.views import RegistrationView as BaseRegistrationView
class ExpRegistrationView(BaseRegistrationView):
form_class = ExpRegistrationForm
Then in urls, after importing views, I add to urlpatterns:
url(r'^../accounts/register/$',views.ExpRegistrationView.as_view(), name='registration_register')
I've shown two attempts above, but I also tried using del or pop to try to get rid of email from my form... And I always get a field error, saying that email is required...
Any suggestions? Or more information needed?
I'm trying to extend Django registration to include my own registration form. In principle this is fairly simple. I just have to write my own form (CustomRegistrationForm) which is a child of the original one (RegistrationForm). Then I can process my specific input by using the user_registered signal of django registration.
So here is what I did:
urls.py:
from django.conf.urls import patterns, include, url
from registration.views import register
from forms import CustomRegistrationForm
from django.contrib import admin
import regbackend
admin.autodiscover()
urlpatterns = patterns('',
url(r'^register/$', register, {'backend': 'registration.backends.default.DefaultBackend', 'form_class': CustomRegistrationForm, 'template_name': 'custom_profile/registration_form.html'},
name='registration_register'),
)
regbackend.py:
from django import forms
from models import UserProfile
from forms import CustomRegistrationForm
def user_created(sender, user, request, **kwargs):
form = CustomRegistrationForm(data=request.POST, files=request.FILES)
if form.is_valid(): # HERE: always fails
user_profile = UserProfile()
user_profile.user = user
user_profile.matriculation_number = form.cleaned_data['matriculation_number']
user_profile.save()
from registration.signals import user_registered
user_registered.connect(user_created)
forms.py:
from models import UserProfile
from django import forms
from registration.forms import RegistrationForm
from django.utils.translation import ugettext_lazy as _
attrs_dict = {'class': 'required'}
class CustomRegistrationForm(RegistrationForm):
matriculation_number = forms.CharField(widget=forms.TextInput(attrs=attrs_dict),
label=_("Matriculation number"),
max_length=12,
error_messages={'invalid': _("This value has to be unique and may contain only numbers.")},
initial=108)
def clean_matriculation_number(self):
print "Validating matriculation number."
data = self.cleaned_data['matriculation_number']
if len(data) != 12:
raise forms.ValidationError(_("Matriculation number consists of exactly 12 digits."))
return data
So the problem is the is_valid() function, because it always returns False. Even if there are no errors! So what is wrong? I spent hours on this one and I have no idea anymore :(
Any help is much appreciated!!
The reason form.is_valid() fails is probably because the form's "clean_username" function checks if the username passed to it already exists. Since the signal is sent after the User object is created and added to the database, the form will fail to pass this test every time. My guess would be if you logged form.cleaned_data after is_valid() returns False, you'll get a list of all of the fields except the username.
form.data might not contain changed values for the fields if the form clean_ function makes any changes (I couldn't find much documentation on form.data).
To avoid the potential problems this might cause, I made two classes for my custom registration form:
# I use this one for validating in my create_user_profile function
class MyRegistrationFormInternal(forms.Form):
# Just an example with only one field that holds a name
name = forms.CharField(initial="Your name", max_length=100)
def clean_name(self):
# (Optional) Change the name in some way, but do not check to see if it already exists
return self.cleaned_data['name'] + ' foo '
# This one is actually displayed
class MyRegistrationForm (MyRegistrationFormInternal):
# Here is where we check if the user already exists
def clean_name(self):
modified_name = super(MyRegistrationForm, self).clean_name()
# Check if a user with this name already exists
duplicate = (User.objects.filter(name=modified_name)
if duplicate.exists():
raise forms.ValidationError("A user with that name already exists.")
else:
return modified_name
Then, instead of using the form.data (which may still be "unclean" in some ways), you can run your POST data through MyRegistrationFormInternal, and is_valid() shouldn't always return false.
I realize this isn't the cleanest solution, but it avoids having to use the (possibly raw) form.data.
Ok I think I solved it (more or less).
I'm still not really sure, why the form did not validate. But as I said I was extending django-registration and the 'register' view already called is_valid() of the form, so I can assume that the form is valid when I process the posted data any futher. The view then calls the backend
backend.register(request, **form.cleaned_data)
with the request and the cleaned data (which is just username, email and password). So I can't use it for registration because my additional information is missing. The backend then fires the signal that I am using and what I did is, is that I created the form again with the provided request. This form, however, will NOT validate (and I tried everything!!) I looked it up, I am doing the exact same thing as django-registration, but it's not working in my code.
So I did not really solve the problem, because the form is still not validating. But I found peace with this, when I realized that the form was already validated by the 'register' view. So I am using form.data[..] instead of form.cleaned_data[..] now which shouldn't be a problem...
Currently, in django.contrib.auth, there can be two users with the username 'john' and 'John'. How can I prevent this from happening.
The most straightforward approach is add a clean method in contib.auth.models and convert it to a lowercase before saving but i dont want to edit the contrib.auth package.
Thanks.
Listen on pre_save for the Users model and then do your checks there. Least intrusive and most portable way.
Here is an example on how this would look like (adapted from the user profile example):
def username_check(sender, instance, **kwargs):
if User.objects.filter(username=instance.username.lower()).count():
raise ValidationError('Duplicate username')
pre_save.connect(username_check, sender=User)
I have used clean method in the user creation form by extending the class UserCreationForm in my custom CustomerRegistrationForm.
The code for the same is as follows:
class CustomerRegistrationForm(UserCreationForm):
# All your form code comes here like creating custom field, etc..
......
......
......
......
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
......
......
......
......
# This is the main centre of interest
# We create the clean_username field specific cleaner method in order to get and set the required field data
# In this case, the username field
def clean_username(self):
username = self.cleaned_data.get('username') # get the username data
lowercase_username = username.lower() # get the lowercase version of it
return lowercase_username
This approach is best as we'll not have to write extra messy code for pre_save event in database, and also, it comes with all django validator functionalities like duplicate username check, etc.
Every other thing will be automatically handled by django itself.
Do comment if there needs to be any modification in this code. T
There is a better option if you are using Postgres. Postgres has a case-insensitive field type, citext. As of 1.11, Django has this available in django.contrib.postgres.fields.citext. You may also need to deal with case-sensitivity in the url regular expressions.
I'd probably solve this on the model using a custom field for username.
from django.db import models
class LowercaseCharField(models.CharField):
"""
Override CharField to convert to lowercase before saving.
"""
def to_python(self, value):
"""
Convert text to lowercase.
"""
value = super(LowercaseCharField, self).to_python(value)
# Value can be None so check that it's a string before lowercasing.
if isinstance(value, str):
return value.lower()
return value
And then in your model..
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils.translation import gettext_lazy as _
# Assuming you saved the above in the same directory in a file called model_fields.py
from .model_fields import LowercaseCharField
class User(AbstractUser):
username = LowercaseCharField(
# Copying this from AbstractUser code
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.'),
validators=[UnicodeUsernameValidator(),],
error_messages={
'unique': _("A user with that username already exists."),
},
)
# other stuff...
Usernames will all get saved as lowercase "automatically".
Updating the accepted answer to be more current:
from django.db.models.signals import pre_save
from django.db.utils import IntegrityError
from django.dispatch import receiver
#receiver(pre_save, sender=User)
def username_check(instance, sender, **kwargs):
"""Ensure that username unique constraint is case insensitive"""
if sender.objects.filter(username__iexact=instance.username.lower()):
raise IntegrityError("Duplicate username")