I have a basic registration/login built that works as intended. The issue I am having is that I can not figure out how to check if a username is already in use and then flash a message that says so.
My route
#app.route("/signup", methods=['GET', 'POST'])
def signup():
form = RegisterForm()
if form.validate_on_submit():
hashed_password = generate_password_hash(form.password.data, method='sha256')
new_user = User(username=form.userName.data, email=form.email.data, password=hashed_password)
db.session.add(new_user)
db.session.commit()
flash("Registration successful...please log in!")
return redirect(url_for('login'))
return render_template("signup.html", form=form)
My User Model
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True)
email = db.Column(db.String(50), unique=True)
password = db.Column(db.String(80))
my HTML (errors div)
{% for field, errors in form.errors.items() %}
<div class="alert alert-error">
{{ form[field].label }}: {{ ', '.join(errors) }}
</div>
{% endfor %}
One possibility is to write a custom validator which queries the database for an already existing user with the name and throws an error if the query is successful.
from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
from wtforms.validators import ValidationError
class RegisterForm(FlaskForm):
# ...
username = StringField(
'Username',
validators=[
DataRequired(),
# ...
])
# ...
def validate_username(self, field):
if User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')
Related
Hi I have written a def clean(self) function in forms to make sure that if there was previously already a post with the same title, they will return a message saying that the title already exists and hence the form cannot be submitted.
Problem now:
When I enter a title that already exists and I try to create the post, all data previously input will be removed and I will be redirected to a fresh form. No errors were raised. What I want is for the error to be raised and shown to the user when I try to click on the create button so all data remains there and the user knows and can change the title before attempting the create the blog post again.
return cleaned_data in forms.py is not defined too...giving a nameerror
Guideline:
Note! There is NO slug field in my form. The slug is only for the url for each individual blogpost. But basically the slug consists of the title. Eg if my username is hello and my chief_title is bye, my slug will be hello-bye. Both the slug and the title has to be unique as you can see in the model, but there is no slug in the form.
models.py
class BlogPost(models.Model):
chief_title = models.CharField(max_length=50, null=False, blank=False, unique=True)
brief_description = models.TextField(max_length=300, null=False, blank=False)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
views.py
def create_blog_view(request):
context = {}
user = request.user
if not user.is_authenticated:
return redirect('must_authenticate')
if request.method == 'POST':
form = CreateBlogPostForm(request.POST or None, request.FILES or None)
if form.is_valid():
obj= form.save(commit = False)
author = Account.objects.filter(email=user.email).first()
obj.author = author
obj.save()
obj.members.add(request.user)
context['success_message'] = "Updated"
return redirect('HomeFeed:main')
else:
form = CreateBlogPostForm()
context['form'] = form
return render(request, "HomeFeed/create_blog.html", {})
forms.py
class CreateBlogPostForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['chief_title']
def clean(self):
chief_title = self.cleaned_data['chief_title']
qs = BlogPost.objects.filter(chief_title=chief_title)
if qs.exists():
raise forms.ValidationError('Post already exists')
return cleaned_data
html
<form class="create-form" method="post" enctype="multipart/form-data">{% csrf_token %}
{% if form.non_field_errors %}
{{form.non_field_errors}}
{% endif %}
<!-- chief_title -->
<div class="form-group">
<label for="id_title">Chief Title!</label>
<input class="form-control" type="text" name="chief_title" id="id_title" placeholder="Title" required autofocus>
</div> {{form.chief_title.errors}}
<button class="submit-button btn btn-lg btn-primary btn-block" type="submit">CREATE</button>
</form>
Well you can create a custom validator to check if the slug exists:
from django.core.exceptions import ValidationError
def validate_slug_exists(value):
blog_post = BlogPost.objects.filter(slug=value)
if blog_post.exists():
raise ValidationError('The post with a given title already exists')
Then in your form add this validator to the fields validators list:
class CreateBlogPostForm(forms.ModelForm):
chief_title = forms.CharField(validators = [validate_slug_exists])
UPDATE
You can also try using validate_unique() in your form class:
def validate_unique(self, exclude=None):
qs = BlogPost.objects.all()
if qs.filter(chief_title=self.chief_title).exists():
raise ValidationError("Blog post with this title already exists")
You can raise a ValidationError as shown in the docs. This would then be displayed in the form for the user.
def clean_slug(self):
slug = slugify(self.cleaned_data.get("chief_title")) if len(self.cleaned_data.get("slug", )) == 0 \
else self.cleaned_data.get("slug", )
if BlogPost.objects.filter(slug=slug).exists():
raise ValidationError(_('Slug already exists.'), code='invalid')
return slug
If you set unique = True Django takes care of checking whether another entry already exists in the database and adds an error in ModelForm.errors.
This is happening inModelForm.validate_unique.
There is no need to bother with this method unless you want to add more info in the error, such as the url of the existing object (it will cost 1 db hit).
Otherwise, the error already exists and you can just return the form with its errors instead of returning a new instance of the form as you currently do in views.py.
Therefore the following views.py as explained in this post should do what you are trying to do:
app/views.py:
def create_blog_view(request):
context = {}
# ...
if request.method == 'POST':
form = CreateBlogPostForm(request.POST or None, request.FILES or None)
if form.is_valid():
# do your thing
else:
context['form'] = form
return render(request, "HomeFeed/create_blog.html", context) # context instead of {}
If you want to get fancier and hit the db once more, you can add the url of the existing object in the errors list as such:
project/settings.py
...
# If you want to provide more info
MY_UNIQUE_BLOGPOST_ERROR_MESSAGE = "This BlogPost already exists"
...
app/models.py
from django.db import models
from django.conf import settings
from django.urls import reverse
class BlogPost(models.Model):
# ...
slug = models.SlugField(
blank=True,
unique=True,
error_messages={"unique": settings.MY_UNIQUE_BLOGPOST_ERROR_MESSAGE),
)
def get_admin_url(self):
return reverse("admin:app_blogpost_change", args=(self.id,))
...
app/forms.py
from django import forms
from django.conf import settings
from django.utils.html import format_html
from itertools import chain
from app.models import BlogPost
class CreateBlogPostForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = '__all__'
def validate_unique(self):
'''
If unique error exists, find the relevant object and return its url.
1 db hit
There is no other need to override this method.
'''
super().validate_unique()
if settings.SHOP_ENTITY_UNIQUE_ERROR in chain.from_iterable(
self.errors.values()
):
instance = BlogPost.objects.get(slug=self.instance.slug)
admin_url = instance.get_admin_url()
instance_name = instance.__str__()
self.add_error(
None,
forms.ValidationError(
format_html(
"This entry already exists: {1}",
admin_url,
instance_name,
),
code="unique",
),
)
I'm creating a staff registration page to create a new user (where NO one has logged in i.e. an Anonymous User). I have a Profile model (adding additional fields such as department, alias etc) which extends from the User model within Django. I've referenced User as a one-to-one relationship to Profile.
When a new staff registers, they have to specify their "stafftypeid" ("Job Title") from the ModelChoiceField referencing Staff_Type table. The User table doesn't store "stafftypeid", so I extended the UserCreationForm.
My problem is that I can successfully submit POST variables via the forms and the User (auth_user) will create a new record. I can see the new User within the Django /admin page. However, Profile will fail to create an accompanying new record and I receive an error ("IntegrityError at /register/
(1048, "Column 'staffTypeID' cannot be null")"). Strangely, I have all the POST variables, but the variables needed for the fields in Profile table are not being passed across.
Is this an inheritance problem? A new Profile record should only be created when you create a new User.
I've tried to follow tutorials and other code from Corey Schafer Django Tutorial 8, Simpleisbetterthancomplex (https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#onetoone) and Stack Overflow.
I've also tried re-writing def form.save() and then split my forms into two (originally just one), since its supposed to be easier to handle on the database side. I'd be really grateful for advice here!
Administrator\models.py: (Staff_Type)
from django.db import models
from decimal import Decimal
class Staff_Type(models.Model):
stafftypeid = models.AutoField(db_column='staffTypeID', primary_key=True)
stafftypedesc = models.CharField(db_column='staffTypeDesc', max_length=150)
class Meta:
ordering = ['stafftypedesc']
unique_together = ('stafftypedesc',)
db_table = 'stafftype'
def __str__(self):
return self.stafftypedesc
Users\models.py:
from django.db import models
from django.contrib.auth.models import User
from decimal import Decimal
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
stafftypeid = models.ForeignKey('Administrator.Staff_Type', models.DO_NOTHING, db_column='staffTypeID')
employeeid = models.CharField(max_length=20)
alias = models.CharField(max_length=20)
department = models.CharField(max_length=150)
organization = models.CharField(max_length=150)
fte = models.DecimalField(max_digits=4, decimal_places=1, default=Decimal(100.0))
def __str__(self):
return f'{self.user.username} Profile'
Users\signals.py:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
Users\register.html:
{% extends "Administrator/adminBase.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-8">
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Register New User </legend>
{{ user_form|crispy }}
{{ profile_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign Up</button>
</div>
</form>
</div>
</div>
{% endblock content %}
Users\forms.py:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from Administrator.models import Staff_Type
from .models import Profile
from .models import Profile
from django.utils.translation import ugettext_lazy as _
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(max_length=150, label = "Email")
first_name = forms.CharField(max_length=150, label = "First Name")
last_name = forms.CharField(max_length=150, label = "Surname")
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2']
class ProfileForm(forms.ModelForm):
stafftypeid = forms.ModelChoiceField(queryset=Staff_Type.objects.all(), empty_label="Staff Type")
employeeid = forms.CharField(max_length=20, label="Employee ID")
alias = forms.CharField(max_length=20, label="Alias")
department = forms.CharField(max_length=150, label="Department")
organization = forms.CharField(max_length=150, label="Organization")
fte = forms.DecimalField(max_digits=4, decimal_places=1, min_value=0.0, max_value=100.0, label="FTE(%)")
class Meta:
model = Profile
fields = ['stafftypeid', 'employeeid', 'alias', 'department', 'organization', 'fte']
Users\views.py:
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm, ProfileForm
def register(request):
if request.method == "POST":
user_form = UserRegisterForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
username = user_form.cleaned_data.get('username')
stafftypeid = profile_form.cleaned_data.get('stafftypeid')
messages.success(request, f'Account created for {username}, with alias: {stafftypeid}')
return redirect('admin-home')
else:
user_form = UserRegisterForm()
profile_form = ProfileForm()
return render(request, 'users/register.html', {'user_form': user_form, 'profile_form': profile_form})
Users\apps.py:
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
The error is happening in your signal. There you just create a Profile and only set the user field, you don't set any of the other fields.
You don't need this signal. You are creating the profile in the separate Profile form and saving it in the view. You should remove both those signals.
Then, update the view:
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
(Separately, please stop naming your ForeignKeys as ending in "_id"; they're not IDs, they are the actual related objects.)
I am making a simple blog , I have used the project name as Blogger_Vip
and app name as blog
I have the following code:
In project level module Blogger_Vip :
urls.py contains:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url , include
from django.conf import settings
from django.conf.urls.static import static
from .views import home_page , contact_page , about_page , register_page ,
login_page
urlpatterns = [
path('admin/', admin.site.urls),
url('^blog/', include('enc_vip_blog.urls',namespace='enc_vip_blog')),
url(r'^$',home_page,name='home'),
url(r'^login/$',login_page,name='login'),
url(r'^register/$',register_page,name='register'),
url(r'^about/$',about_page,name='about'),
url(r'^contact/$',contact_page,name='contact'),
]
views.py contains:
from django.shortcuts import render , redirect
from django.http import HttpResponse
from .forms import ContactForm,LoginForm,RegisterForm
from django.contrib.auth import authenticate,login
from django.contrib.auth import get_user_model
def home_page(r):
context = {
'title':'Hello World!',
'content':'Welcome to the Home Page!'
}
if r.user.is_authenticated:
context['premium_content']='Yeahhhh!'
return render(r,'home_page.html',context)
def about_page(r):
context = {
'title': 'ABOUT PAGE',
'content': 'Welcome to the About Page!'
}
return render(r,'about_page.html',context)
def contact_page(r):
contact_form = ContactForm(r.POST or None)
context = {
'title': 'CONTACT PAGE',
'content': 'Welcome to the Contact Page!',
'form':contact_form,
'brand':'new brand',
}
if contact_form.is_valid():
print(contact_form.cleaned_data)
return render(r,'contacts/view.html',context)
User = get_user_model()
#This captures the default fields of User Model
def register_page(r):
register_form = RegisterForm(r.POST or None)
context = {
'register_form':register_form
}
if register_form.is_valid():
username = register_form.cleaned_data.get('username')
email = register_form.cleaned_data.get('email')
password = register_form.cleaned_data.get('password')
new_user = User.objects.create_user(username,email,password)
print(new_user)
# print(password)
return render(r,'auth/register.html',context)
def login_page(r):
login_form = LoginForm(r.POST or None)
context = {
'login_form':login_form
}
print('User Logged in :')
print(r.user.is_authenticated)
if login_form.is_valid():
print(login_form.cleaned_data)
username = login_form.cleaned_data.get('username')
password = login_form.cleaned_data.get('password')
user = authenticate(r,username=username,password=password)
print("User Logged in:")
print(r.user.is_authenticated)
print(user)
if user is not None:
print(r.user.is_authenticated)
login(r,user)
# login_form['login_form']=login_form
return redirect('/')
else:
print('Error')
return render(r,'auth/login.html',context)
forms.py contains:
from django import forms
from django.core.validators import ValidationError
from django.contrib.auth import get_user_model
User = get_user_model()
class ContactForm(forms.Form):
fullname = forms.CharField(max_length=100,widget=forms.TextInput(
attrs={'class':'form-
control','placeholder':'Your name here',
'id':'fullname'}
),help_text="Max 100
characters")
email = forms.EmailField(widget=forms.TextInput(
attrs={'class':'form-
control','placeholder':'Your email here',
'id':'email'}
))
content = forms.CharField(max_length=300,widget=forms.Textarea(attrs=
{'class':'form-control',
'placeholder':'Leave
your message here',
'id':'content'}),
help_text='300 characters max')
def clean_email(self):
email = self.cleaned_data.get('email')
if not 'gmail.com' in email:
raise forms.ValidationError("Email must be Gmail id!")
return email
class RegisterForm(forms.Form):
username = forms.CharField()
email = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(label="Confirm
Password",widget=forms.PasswordInput)
#Unique username constraint setup
def clean_username(self):
username = self.cleaned_data.get('username')
#Queryset to get all logged in or registered user objects from User
model
user_queries = User.objects.filter(username=username)#Got all usernames
that are already registered
#Checking if current username matches any other username from the
results
if user_queries.exists():
raise forms.ValidationError('Username Exists!')
return username
# Unique email id constraint setup
def clean_email(self):
email = self.cleaned_data.get('email')
email_queries = User.objects.filter(email=email)
if email_queries.exists():
raise forms.ValidationError('This Email Id is taken!')
return email
#Password confirmation during registration
def clean(self):
data = self.cleaned_data
password = self.cleaned_data.get('password')
confirmation_password = self.cleaned_data.get('confirm_password')
if password!=confirmation_password:
raise forms.ValidationError("Passwords must match!")
return data
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
Now I have an app called as enc_vip_blog:
This contains the following:
from django.db import models
from django.urls import reverse
# Create your models here.
class EntryManager(models.Manager):
def published(self):
return self.get_queryset().filter(publish = True)
class Tag(models.Model):
slug = models.SlugField(max_length=200 , unique=True)
def __str__(self):
return self.slug
class Entry(models.Model):
title = models.CharField(max_length=100)
body = models.TextField(max_length=1000)
short_body = models.CharField(max_length=200,default='Click link to know
more')
slug = models.SlugField(max_length=100 , unique=True)
publish = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
image = models.ImageField(upload_to='blog/',null=True,blank=True)
tags = models.ManyToManyField(Tag)
objects = EntryManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('enc_vip_blog:detail',kwargs={'slug':self.slug})
#To make admin representations look better override class Meta
class Meta:
verbose_name = 'Blog Entry'
verbose_name_plural = 'Blog Entries'
#Set ordering to reverse chronological order
ordering = ['-created']
urls.py contains:
from django.conf.urls import url
from .views import BlogListView ,BlogDetailView
app_name = 'enc_vip_blog'
urlpatterns = [
url(r'^$', BlogListView.as_view() , name='list'),
url(r'^(?P<slug>\S+)$', BlogDetailView.as_view() , name='detail'),
]
Finally to list all I have list.html:
{%extends 'base/base.html'%}
{%load static%}
{%block content%}
<ul>{%for object in object_list%}
{%include 'blog/card.html' with instance=object%}
{%endfor%}
</ul>
{%endblock%}
and card.html:
<div class="card-body">
<h5 class="card-title"> <a href="{{ instance.get_absolute_url }}">{{
instance.title }}</a></h5>
{%if instance.image%}
<a href="{{ instance.get_absolute_url }}" >
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
<img class="card-img-top" src="{{ instance.image.url }}" style="width:
100%;" alt="{{ instance.title }}">
</div>
<div class="col-md-4"></div>
</div>
</a>
{%endif%}
<p class="card-text">{{ instance.short_body }}</p>
<p class="Meta">{{ instance.created }}</p>
<p class="Meta">tagged under {{ instance.tags.all|join:', ' }}</p>
<a href="{{ instance.get_absolute_url }}" class="btn btn-
primary">View</a>
</div>
</div>
I want that when the user logs in he/she shud be redirected to homepage and homepage must contain a link saying 'Visit Blog'
there I have used {%url 'enc_vip_blog:list'%}
and i get the following errors:
django.urls.exceptions.NoReverseMatch: Reverse for 'detail' not found.
'detail' is not a valid view function or pattern name.
I am trying to find out a link to this detail kind of think...
Also the blog was runs fine if I donot use the namespace stuff
But still on homepage even hardcoding the url section into href as
{%url 'blog/'%} didn't work?
Can anybody help?
Help me with both the problem for namespace and also the hardcoding kind of stuff?
Try url(r'^(?P<slug>[\w-]+)/$', views.BlogDetailView.as_view(), ...)
And make sure you have slugs in your db just check in the admin panel
Altough my form to send an offer is correctly displayed and I have no error when submitting the offers, they are not saved in my database.
I know that by checking the admin site, no objects are saved.
But on the other hand, I already have written the code for the registration and users are saved in the database.
I suspect the ForeignKey relation between my 2 models as the culprit.
models.py
from django.db import models
class User(models.Model):
username = models.CharField(max_length=30, unique=True, blank=False)
password1 = models.CharField(max_length=40, blank=False)
password2 = models.CharField(max_length=40, blank=False)
mail = models.EmailField(unique=True, blank=False)
birthday = models.DateField(blank=False)
date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date d'inscription")
def __str__(self):
return self.username
class Offer(models.Model):
publisher = models.ForeignKey(User)
content = models.TextField()
date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")
def __str__(self):
return self.publisher.username
forms.py
from django import forms
from django.contrib import admin
from django.contrib.auth.hashers import make_password, check_password
from django.utils.translation import ugettext_lazy as _
from myuser.models import User, Offer
class UserCreationForm(forms.ModelForm):
class Meta:
model = User
widgets = {
'password1' : forms.PasswordInput(),
'password2' : forms.PasswordInput(),
}
fields = ("username", "password1", "password2", "mail")
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("les mots de passes ne correspondent pas")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.password1 = make_password(self.cleaned_data["password1"])
user.password2 = make_password(self.cleaned_data["password2"])
if commit:
user.save()
return user
class LoginForm(forms.Form):
username = forms.CharField(label="nom d'utilisateur")
password = forms.CharField(label="mot de passe",
widget = forms.PasswordInput)
def clean(self):
cleaned_data = super(LoginForm, self).clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
user = User.objects.get(username=username)
if check_password(password, user.password1):
return cleaned_data
else:
raise forms.ValidationError("Le nom d'utilisateur et le mot de passe ne correspondent pas")
class SendOfferForm(forms.ModelForm):
class Meta:
model = Offer
fields = ('content',)
Views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse
from django.core.exceptions import ObjectDoesNotExist
from myuser.models import User, Offer
from myuser.forms import UserCreationForm, LoginForm, SendOfferForm
def get_logged_user_from_request(request):
if 'logged_user_id' in request.session:
logged_user_id = request.session['logged_user_id']
return User.objects.get(id=logged_user_id)
else:
return None
def register(request):
registered = False
if request.method == 'POST':
user_form = UserCreationForm(data=request.POST)
if user_form.is_valid():
user = user_form.save()
registered = True
else:
print(user_form.errors)
else:
user_form = UserCreationForm()
return render(request,
'myuser/create_account.html',
{'user_form': user_form, 'registered': registered} )
def login(request):
if request.method=='POST':
form = LoginForm(request.POST)
try:
if form.is_valid():
user = User.objects.get(username=request.POST.get('username'))
logged_user = User.objects.get(username=request.POST.get('username'))
request.session['logged_user_id'] = logged_user.id
return HttpResponseRedirect('/')
else:
error = "le nom d'utilisateur et le mot de passe ne correspondent pas"
return HttpResponse("Invalid login details supplied.")
except User.DoesNotExist:
return HttpResponse("Invalid login details supplied.")
else:
form = LoginForm
return render(request, 'myuser/mylogin.html', locals())
def send_offer(request):
sent = False
logged_user = get_logged_user_from_request(request)
if logged_user:
if request.method == 'POST':
try:
offerform = SendOfferForm(request.POST, instance=logged_user)
if offerform.is_valid():
sent = True
offerform.save()
else:
print(offerform.errors)
except:
return HttpResponse("drapeau except")
else:
offerform = SendOfferForm(instance=logged_user)
else:
return HttpResponse("Vous n'ĂȘtes pas connectĂ©")
return render(request, 'myuser/send_offer.html', locals())
urls.py
from django.conf.urls import patterns, url, include
from django.views.generic import TemplateView
urlpatterns = patterns('myuser.views',
url(r'^inscription/$', 'register', name='create_account'),
url(r'^connexion/$', 'login', name='login'),
url(r'^envoyer_une_offre$', 'send_offer', name='send_offer'),
)
send_offer.html
{% extends "base.html" %}
{% block content %}
<h1> Offer </h1>
{% if not sent %}
<p> write your offer <p/>
<form action="{% url "send_offer" %}" method='POST' class='sendofferform'>
{{ form.errors }}
{{ form.non_field_errors }}
{% csrf_token %}
{{ offerform.as_p }}
<input type="submit" value="Submit" />
</form>
{% else %}
Offer is published
publish another offer?<br />
get back to the homepage<br />
{% endif %}
{% endblock %}
admin.py
from django.contrib import admin
from myuser.models import User, Offer#, Message
class UserAdmin(admin.ModelAdmin):
list_display = ('id', 'username', 'status', 'college', 'apercu_description')
list_filter = ('id', 'username', 'birthday')
date_hierarchy = 'date'
ordering = ('date', )
search_fields = ('username', 'description')
def apercu_description(self, User):
text = User.description[0:40]
if len(User.description) > 40:
return '%s' % text
else:
return text
class OfferAdmin(admin.ModelAdmin):
list_display = ('id', 'publisher', 'apercu_offre')
list_filter = ('id', )
date_hierarchy = 'date'
ordering = ('date', )
search_fields = ('publisher',)
def apercu_offre(self, Offer):
text = Offer.content[0:40]
if len(Offer.content) > 40:
return '%s' % text
else:
return text
admin.site.register(User, UserAdmin)
admin.site.register(Offer, OfferAdmin)
All of the other function works (register and login) and the register() function correctly saves the user but the send_offer() function, which is very similar to the register function doesn't work and after searching on internet for hours, I still have no idea why the offers are not saved.
But when I try to add an offer in the admin site, it seems to work.
Besides, I tried to save an offer in the python manage.py shell :
>>> Offer.publisher = "a"
>>> Offer.content = "lalalala"
>>> Offer.save()
Traceback (most recent call last):
File "/usr/local/lib/python3.4/dist-packages/django/core/management/commands/shell.py", line 69, in handle
self.run_shell(shell=options['interface'])
File "/usr/local/lib/python3.4/dist-packages/django/core/management/commands/shell.py", line 61, in run_shell
raise ImportError
ImportError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.4/code.py", line 90, in runcode
exec(code, self.locals)
File "<console>", line 1, in <module>
TypeError: save() missing 1 required positional argument: 'self'
EDIT : The solution was to add these lines :
if offerform.is_valid():
sent = True
offer = offerform.save(commit=False)
offer.publisher = User.objects.get(id=logged_user.id)
offer.save()
Please, note that logged_user is a function, which is described in view.py.
On a ModelForm if you don't pass any object when its instantiated It will create a new object in the database of the specified type. So in this case on your SendOfferForm you have specified Offer. IF you do pass an existing object it has to be the specified type in this case Offer.
It looks like you are trying to automatically fill the publisher field with the currently logged in user. In order to do this you need to manually set the publisher field to an instance of User.
If you intend for the form to create a new Offer in the database do this
offerform = SendOfferForm(request.POST)
If you are trying to update a current offer THEN you would need to pass an instance object into the form
offerform = SendOfferForm(request.POST, instance=someOffer)
To manually add the user field call save on your form with commit=False this will return an offer object WITHOUT saving to the DB. At this point you have a chance to customize the data in object and then save.
offer = offerform.save(commit=False)
offer.publisher = request.user.id
offer.save()
All of this is explained in more detail in the django documentation
ModelForm documentation
I'm trying to ask a user some additional info while signing up. I'm using django allauth for authorization and authentication. I try to add three more fields during the signup process. If If I run it, it shows me the standard form plus gender field. However, it doesn't seem to really work. How can I save the data? Could someone help? Thank you in advance!
EDITED: if I just use
if form.is_valid():
form.save()
return redirect('/success/')
I get an error:
save() missing 1 required positional argument: 'user'
I'm quite new to django.
I created signups app in the project.
I put this in allauth_settings.py:
ACCOUNT_SIGNUP_FORM_CLASS = 'signups.forms.MySignupForm'
My signups/model.py:
from django.contrib.auth.models import User
from django.db import models
from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount
import hashlib
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
about_me = models.TextField(null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add= True, auto_now=False)
updated = models.DateTimeField(auto_now_add= False, auto_now=True)
GENDER_CHOICES = (
('m', 'Male'),
('f', 'Female'),
)
# gender can take only one of the GENDER_CHOICES options
gender = models.CharField(max_length=1, choices=GENDER_CHOICES,
verbose_name='Gender')
def __unicode__(self):
return self.user.username
class Meta:
db_table = 'user_profile'
def profile_image_url(self):
"""
Return the URL for the user's Facebook icon if the user is logged in via
Facebook, otherwise return the user's Gravatar URL
"""
fb_uid = SocialAccount.objects.filter(user_id=self.user.id, provider='facebook')
if len(fb_uid):
return "http://graph.facebook.com/{}/picture?width=40&height=40".format(fb_uid[0].uid)
return "http://www.gravatar.com/avatar/{}?s=40".format(hashlib.md5(self.user.email).hexdigest())
def account_verified(self):
"""
If the user is logged in and has verified hisser email address, return True,
otherwise return False
"""
if self.user.is_authenticated:
result = EmailAddress.objects.filter(email=self.user.email)
if len(result):
return result[0].verified
return False
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
my signups/forms.py:
from allauth.account.forms import SignupForm
from django import forms
from .models import UserProfile
class MySignupForm(SignupForm):
class Meta:
model = UserProfile
gender = forms.CharField(max_length=1, label='gender')
def save(self, user):
user.gender = self.cleaned_data['gender']
user.save()
my signups/views.py:
from django.template import RequestContext
from django.shortcuts import render_to_response
from .forms import SignupForm
def index(request):
form = MySignupForm(request.POST or None)
if form.is_valid:
???
return render_to_response("signups/index.html", locals(),
context_instance=RequestContext(request))
My index.html is very basic, I just wanted to see the representation of the form:
{% extends 'account/base.html' %}
{% block head_title %}ProjectName{% endblock %}
{% block content %}
<form method="POST" action="">
{{ form.as_p }}
<input type="submit">
</form>
{% endblock %}
You are instantiating the SignupForm, which is the standard form but not your MySignupForm in the view. Change it like this:
def index(request):
form = MySignupForm()
return render_to_response("signups/index.html", locals(),
context_instance=RequestContext(request))