I am trying to validate the uniqueness of an email address in datastore. The problem is that this does not allow me to edit/update the entry (e.g. if I only want to change the display_name - see models.py below).
I am submitting a form build using djangoforms (with an datastore entry prefilled as I already know the key of the datastore entry):
forms.UserForm(instance=db.get(db.Key(key)))
After submitting the form using POST method I get the details from datastore and associate these with the submitted form:
entry = db.get(db.Key(self.request.get('key')))
data = forms.UserForm(data=self.request.POST, instance=entry)
if data.is_valid():
...
The form then validates using the following form class (in forms.py):
from django import forms
from google.appengine.ext.db import djangoforms
import models
class UserForm(djangoforms.ModelForm):
class Meta:
model = models.AuthorizedUsers
def clean_email(self):
"""Prevent duplicate email addresses."""
if self.Meta.model.all().filter('email =', self.cleaned_data['email']).count():
raise forms.ValidationError('Duplicate Entry: %s.' %
self.cleaned_data['email'])
return self.cleaned_data['email']
Using the following model (in models.py)
class AuthorizedUsers(db.Model):
"""Base model class for storing user permissions."""
email = db.StringProperty()
display_name = db.StringProperty()
Any suggestions what I am missing here? How can I prevent the raising of ValidationError when just updating an entry? Keep in mind that I do want to prevent the adding of a new datastore entry with the same email address.
Thanks in advance for your help!
You can check to see if your email field has changed before checking whether or not to validate it against all previous email addresses. Your clean method would be changed to:
def clean_email(self):
"""Prevent duplicate email addresses."""
if 'email' in self.changed_data:
if self.Meta.model.all().filter('email =', self.cleaned_data['email']).count():
raise forms.ValidationError('Duplicate Entry: %s.' %
self.cleaned_data['email'])
return self.cleaned_data['email']
Related
I am using Flask-Admin for my Flask-based project. In it, I have some models (using peewee) where the primary-key is user-set, such as username for a User. However Flask-Admin is not showing these fields in the model's create/edit pages.
Now, when I try to create a new user, the "Save" button gives a peewee.UserDoesNotExist error, and the "Save & Add" says "Record successfully created" twice but doesn't actually do anything.
I had extended the save() method to auto-generate the username from the name if it's unset, but the problem persisted even when I removed the overriding.
The code...
Here's what my User model looks like:
# import peewee as pw
class User(BaseModel, UserMixin):
username = pw.CharField(32, primary_key=True)
password = pw.CharField(512, null=True)
name = pw.CharField(64)
# ... other fields not shown ... #
def save(self, *args, **kwargs):
# Set the username if field is blank
if self.username == 'auto' or not self.username:
self.username = self.name.replace(' ', '').lower()
# Do the real save
super(User, self).save(*args, **kwargs)
Here's my admin code:
# from flask_admin.contrib.peewee.view import ModelView
class AdminModelUser(ModelView):
can_create = True
column_list = ('username', 'name', 'group', 'active')
admin.add_view(AdminModelUser(User, name='Users', category='Accounts'))
Trying out stuff
I later tried to override the get_form() method, to use wtfpeewee directly and allow the pk, like this:
# from wtfpeewee.orm import model_form
class AdminModelUser(ModelView):
...
def get_form(self):
return model_form(User, allow_pk=True)
Now the field is showing, but saving still does not work. When I edit the username of an existing user, the admin says "Record was successfully saved", but it doesn't get saved. And when I try to create a new user, I still get a peewee.UserDoesNotExist error.
My guess is that I've done the overriding in the wrong place, with the fields showing in the form but not in the save methods. I couldn't find any mention of this in the docs: does anyone know how to do it?
When you've got a non-integer primary key, you must call save() with force_insert=True to add a new row.
http://docs.peewee-orm.com/en/latest/peewee/models.html#non-integer-primary-keys-composite-keys-and-other-tricks
I just started developing with Django and I'm having a bit of an issue with the use of a form I've created.
Currently I have a model named SignUp which allows users to "sign up" on the site by inputting their first and last names and their email address.
A feature I'm trying to implement deletes a user who is already signed up by the user submitting the email address they signed up with.
I have a form set up that's just a simple email field but I can't seem to figure out how to get my view to match up the form to the user and then successfully delete the user from the database.
Here is the code for the form:
class DeleteUserForm(forms.Form):
email_address = forms.EmailField()
And here is the code for the view:
class DeleteUserView(generic.edit.FormView):
template_name = 'signups/delete_user_form.html'
form_class = DeleteUserForm
success_url = '/users/delete/success/'
def form_valid(self, form):
for user in SignUp.objects.all():
if user.email == form: # The email attribute holds the user's
user.delete() # email address and the form should be
# the address the user submitted to be
# deleted?
return redirect(self.success_url)
return redirect('/users/delete/failure/')
Every time I try to submit an email address, I get no match, no user is deleted, and I just get redirected to my failure directory.
Anybody have an idea of what's going on?
Thanks
Using Kamil's logic, I rewrote the field lookup and this code works:
duplicates = SignUp.objects.filter(email__iexact=form.cleaned_data['email_address'])
if duplicates:
duplicates.delete()
return redirect(self.success_url)
But does anyone know why the code I was using before didn't work?
Simplest solution:
duplicates = SignUp.objects.filter(email=form.cleaned_data['email_address'])
if duplicates:
duplicates.delete()
return redirect(self.success_url)
return redirect('/users/delete/failure/')
Right, fixed the relation lookup.
You can access field values via form.cleaned_data dictionary:
if user.email == form.cleaned_data['email_address']:
I came across this code:
drinker/models.py:
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class Drinker(models.Model):
user = models.OneToOneField(User)
birthday = models.DateField()
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
drinker/forms.py:
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
from drinker.models import Drinker
class RegistrationForm(ModelForm):
username = forms.CharField(label=(u'User Name'))
email = forms.EmailField(label=(u'Email Address'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
password1 = forms.CharField(label=(u'Verify Password'), widget=forms.PasswordInput(render_value=False))
class Meta:
model = Drinker
exclude = ('user',)
def clean_username(self):
username = self.cleaned_data['username']
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError("That username is already taken, please select another.")
def clean(self):
if self.cleaned_data['password'] != self.cleaned_data['password1']:
raise forms.ValidationError("The passwords did not match. Please try again.")
return self.cleaned_data
My Question is about the inner class meta which as two attributes:
model=Drinker
exclude=('user`,)
I have a not-so-clear understanding of how this meta class work. I have read the documentation but I am still confused. Can you kindly explain what those two lines mean and what their purpose is?
Thanks
The exclude attribute tells Django what fields from the model not to include in the form.
Quoting the Selecting fields to use section of the model form documentation:
2. Set the exclude attribute of the ModelForm’s inner Meta class to a list of fields to be excluded from the form.
The model line simply tells Django what model to take the fields from; together the two lines tell Django to give RegistrationForm fields based on all fields on the Drinker model, except 'user'. For the given Drinker model, that's birthday and name.
These fields are added to the other form fields already defined on the form. If the Drinker model gained more fields, those would automatically be part of the form too.
See the Overriding the default fields section of the same chapter:
When you explicitly instantiate a form field like this, it is important to understand how ModelForm and regular Form are related.
ModelForm is a regular Form which can automatically generate certain fields. The fields that are automatically generated depend on the content of the Meta class and on which fields have already been defined declaratively. Basically, ModelForm will only generate fields that are missing from the form, or in other words, fields that weren’t defined declaratively.
The inner Meta class is just a convenient way to create a namespace for such configuration on your form class for the Django framework to find. All Django now has to do is introspect Form.Meta and see what attributes are defined there.
Note that using exclude can lead to security problems. From the same documenation:
It is strongly recommended that you explicitly set all fields that should be edited in the form using the fields attribute. Failure to do so can easily lead to security problems when a form unexpectedly allows a user to set certain fields, especially when new fields are added to a model. Depending on how the form is rendered, the problem may not even be visible on the web page.
The alternative approach would be to include all fields automatically, or blacklist only some. This fundamental approach is known to be much less secure and has led to serious exploits on major websites (e.g. GitHub).
fields = exclude() and fields = '__all__' - means display all the fields
exclude = ('password',) - means exclude password field
fields = ('user','email',) - means display only email field and userfield
in short : fields you want to show up in the form should be mentioned in 'fields' attribute ex:
fields = '__all__' #will show all the fields from the model in the form
'exclude' does the opposite
exclude = ['title'] # don't show the title field
I got suddenly an integrity error: "Duplicate entry 'qw' for key 'username'
from this model. 1062,
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
class Customer(User):
slug=models.SlugField(unique=True)
description=models.TextField(null=True)
phone=models.IntegerField(null=True)
id_verified=models.NullBooleanField()
picture=models.ImageField(upload_to='media/customer', null=True)
isWorker=models.BooleanField()
def save(self,*args,**kwargs):
self.slug=slugify(self.username)
super(Customer,self).save(*args, **kwargs)
def __unicode__(self):
return self.username
What's wrong here?
From the docs, about specifying a custom user model:
You must then provide some key implementation details:
USERNAME_FIELD
A string describing the name of the field on the User
model that is used as the unique identifier. This will usually be a
username of some kind, but it can also be an email address, or any
other unique identifier. The field must be unique (i.e., have
unique=True set in its definition).
So username seems to be unique, at least until you set a custom username field.
Read the docs
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model
I am new to Django and I want to make a user registration form in Django. While creating model, I gave a fieldtype->PasswordField() to password field but when I run this model into terminal I got a error
password=models.PasswordField()
AttributeError: 'module' object has no attribute 'PasswordField'
So I want to know that which field type is used for password in Django.I already refer to form doc but I found nothing.
so please help me
Theory
Use django.db.models.CharField, since this is an OK database column type for a password string
Use django.forms.PasswordWidget, to represent this field in a form, see overriding default form field widgets
Example
models.py:
from django.db import models
class YourModel(models.Model):
password = models.CharField(max_length=200)
forms.py:
from django import forms
from models import YourModel
class YourModelForm(forms.ModelForm):
class Meta:
widgets = {'password': forms.PasswordField}
model = YourModel
There is no PasswordField() in django. You can to use CharField() to store a password, but to show it as password input type in a html form you can specify a widget to it.
Example:
password = forms.CharField(widget=forms.PasswordInput())
More reference at :PasswordInput