basic unique ModelForm field for Google App Engine - python

I do not care about concurrency issues.
It is relatively easy to build unique form field:
from django import forms
class UniqueUserEmailField(forms.CharField):
def clean(self, value):
self.check_uniqueness(super(UniqueUserEmailField, self).clean(value))
def check_uniqueness(self, value):
same_user = users.User.all().filter('email', value).get()
if same_user:
raise forms.ValidationError('%s already_registered' % value)
so one could add users on-the-fly. Editing existing user is tricky. This field would not allow to save user having other user email. At the same time it would not allow to save a user with the same email. What code do you use to put a field with uniqueness check into ModelForm?

quick and dirty way would be:
make check_uniqueness classmethod
use custom field check in ModelForm, like this:
class User(forms.ModelForm):
email = forms.EmailField()
def clean_email(self):
data = self.cleaned_data['email']
original = self.instance.email
if original == data:
return data
UniqueUserEmailField.check_uniqueness(data)
return data
better options?

Related

MongoEngine - Call function only when document is being created or specific field being set?

I'm not sure what is the best/pythonic way of having a User document that automatically hashes its password upon creating.
Consider the following mongoengine model :
class User(Document):
email = EmailField(required=True, primary_key=True)
name = StringField(required=True)
pswd = StringField(required=True)
def check_pswd(self, password):
return verify_password(password, self.pswd)
def hash_pswd(self, password):
return hash_password(password):
def save(self, *args, **kwargs):
self.pswd = self.hash_pswd(self.pswd)
super().save(*args, **kwargs)
When I create a user, it works fine :
user = User()
user.email = 'user#email.com'
user.pswd = 'password'
user.name = 'User'
user.save()
Buf if I update it, it will double hash its password, I don't want that.
#User wants to change his name
user = User.objects(email='user#email.com')
user.name = 'User 2'
user.save()
Is there a way for me to hash its password only when creating or changing the password?
Or maybe I should delegate the responsibility of hashing the password to the View/Controller?
I am not giving you the code sample, you can use Document.update() method which will update the fields that only has changed.
If you still wanna use the save method,
Then you can create a logic along the following lines.
Check if the user has changed the password(by comparing the existing stored hash and new hash if any)
If the new hash is different then user has changed the password in that case you can push an Document.update method.
If not, don't call update on that field.
Alternatively update in Mongoengine accepts a iterable, so you can simply create a list or a dictionary object and convinently choose to Remove the password hash field from it.
As for who should execute this i.e View / Controller, its a Design decision but I would rather keep Representation (GUI / Front End) seperate from logic, so I would delegate this to the controller or even more Intrinsically to the Object who is responsible for handling all database/network related tasks, this way it would be isolated and easy to modify. And would not complexify or slow the View objects process / thread
Link for update using an iterable like Dict.
stackoverflow question for Mongoengine update using iterable
Link discussing save methods deprecation (The Maintainer Has commented below as save method not being deprecated, so trust him/her and proceed at will on this link)
Mongoengine save method deprecated?
Link for update method in mongoengine.
Mongoengine Atomic Update

What is best practice for restricting user permissions when editing model instances

I am building a simple app using User Authentication.
My app has 3 models:
Users : The standard Django user model
Locations: A model for an office (address, site name, etc)
Employees: A model for an employee (name, email, etc)
I also have a series of views that allow a user to login, create, and edit locations/sites, etc.
What I want to know, is what is the best practice to restrict editing of model instances to those which the user has created? E.g. with no amendment, two users could create data and both could edit the others. How do I restrict this to editing their own?
I know the long form way is to put a ForeignKey(User) on each model to restrict the view with a queryset, but this seems lengthy and cumbersome. Is there a Django trick I am missing? Perhaps a decorator?
What's the best practice?
The easiest way would be to edit your models so that they have an owner or user field that is a ForeignKey to the creator.
class Locations(models.Model):
owner = ForeignKey(User)
...
And in your views:
def edit_location(request, location_id):
location = Locations.objects.get(pk=location_id)
if request.user is not location.owner:
# return a 401 or redirect to somewhere
else:
# do stuff
You can use Django's ManyToManyField option if each Location and User can have multiple relationships. For example:
class Locations(models.Model):
owners = models.ManyToManyField(User)
user = User.objects.create(username='Ian')
location = Locations.objects.create(...)
location.owners.add(user)
And then the User/Locations are available on both:
>>> location.owners.all()
[<User: Ian>]
>>> user.locations_set.all()
[<Locations: ...>]
The set on User will be automatically created and it will be named <Model Name>_set.
You can then use the in operator to check ownership:
def edit_location(request, location_id):
location = Locations.objects.get(pk=location_id)
if request.user in location.owners.all():
# return a 401 or redirect to somewhere
else:
# do stuff

Django custom registration fields

I'm becoming increasingly bewildered by the range of answers on offer to the seemingly simple problem of adding custom fields to the django-registration register form/flow. This should be a default, documented aspect of the package (not to sound ungrateful, just that it is such a well-equipped package), but solutions to the problem are dizzying.
Can anyone give me the most simple solution to getting UserProfile model data included in the default registration register page?
Update:
I eventually used Django Registration's own signals to give me this hacky fix. It is particularly ugly because, I had to use try on the POST attribute dealing with my Boolean since I found that the checkbox returned nothing if left empty.
Would appreciate any advice on improving this, or best practice.
My app / models.py
from registration.signals import user_registered
from django.dispatch import receiver
class UserProfile(models.Model):
user = models.OneToOneField(User)
event_commitments = models.ManyToManyField(Event, null=True, blank=True)
receive_email = models.BooleanField(default=True)
#receiver(user_registered)
def registration_active_receive_email(sender, user, request, **kwargs):
user_id = user.userprofile.id
user = UserProfile.objects.get(pk=user_id)
try:
if request.POST['receive_email']:
pass
except:
user.receive_email = False
user.save()
Registration app / forms.py
class RegistrationForm(forms.Form):
# default fields here, followed by my custom field below
receive_email = forms.BooleanField(initial=True, required=False)
Thanks
What you have looks like a workable approach.
I've looked through the django-registration code, and based on the following comments in the register view I've come up with another solution. I'm not totally sure this is cleaner, but if you aren't a fan of signals this is good. This also provides a much easier avenue if you intend to make more customizations.
# from registration.views.register:
"""
...
2. The form to use for account registration will be obtained by
calling the backend's ``get_form_class()`` method, passing the
``HttpRequest``. To override this, see the list of optional
arguments for this view (below).
3. If valid, the form's ``cleaned_data`` will be passed (as
keyword arguments, and along with the ``HttpRequest``) to the
backend's ``register()`` method, which should return the new
``User`` object.
...
"""
You could create a custom backend and override those mentioned methods:
# extend the provided form to get those fields and the validation for free
class CustomRegistrationForm(registration.forms.RegistrationForm):
receive_email = forms.BooleanField(initial=True, required=False)
# again, extend the default backend to get most of the functionality for free
class RegistrationBackend(registration.backends.default.DefaultBackend):
# provide your custom form to the registration view
def get_form_class(self, request):
return CustomRegistrationForm
# replace what you're doing in the signal handler here
def register(self, request, **kwargs):
new_user = super(RegistrationBackend, self).register(request, **kwargs)
# do your profile stuff here
# the form's cleaned_data is available as kwargs to this method
profile = new_user.userprofile
# use .get as a more concise alternative to try/except around [] access
profile.receive_email = kwargs.get('receive_email', False)
profile.save()
return new_user
To use the custom backend, you can then provide separate urls. Before including the default urls, write 2 confs that point at your custom backend. Urls are tested in the order defined, so if you define these two before including the defaults, these two will capture before the default ones are tested.
url(r'^accounts/activate/(?P<activation_key>\w+)/$',
activate,
{'backend': 'my.app.RegistrationBackend'},
name='registration_activate'),
url(r'^accounts/register/$',
register,
{'backend': 'my.app.RegistrationBackend'},
name='registration_register'),
url(r'^accounts/', include('registration.backends.default.urls')),
The docs actually describe all this, but they aren't particularly accessible (no readthedocs). They are all included in the project, and I was browsing them here.
I eventually used Django Registration's own signals to give me this fix.
I will clean up the try/except flow at some point. dokkaebi also points out above that I might be able to assess the request.GET parameters for when a checkbox is left empty.
My app / models.py
from registration.signals import user_registered
from django.dispatch import receiver
class UserProfile(models.Model):
user = models.OneToOneField(User)
event_commitments = models.ManyToManyField(Event, null=True, blank=True)
receive_email = models.BooleanField(default=True)
#receiver(user_registered)
def registration_active_receive_email(sender, user, request, **kwargs):
user_id = user.userprofile.id
user = UserProfile.objects.get(pk=user_id)
try:
if request.POST['receive_email']:
pass
except:
user.receive_email = False
user.save()
Registration app / forms.py
class RegistrationForm(forms.Form):
# default fields here, followed by my custom field below
receive_email = forms.BooleanField(initial=True, required=False)

Case insensitive unique model fields in Django?

I have basically a username is unique (case insensitive), but the case matters when displaying as provided by the user.
I have the following requirements:
field is CharField compatible
field is unique, but case insensitive
field needs to be searchable ignoring case (avoid using iexact, easily forgotten)
field is stored with case intact
preferably enforced on database level
preferably avoid storing an extra field
Is this possible in Django?
The only solution I came up with is "somehow" override the Model manager, use an extra field, or always use 'iexact' in searches.
I'm on Django 1.3 and PostgreSQL 8.4.2.
As of Django 1.11, you can use CITextField, a Postgres-specific Field for case-insensitive text backed by the citext type.
from django.db import models
from django.contrib.postgres.fields import CITextField
class Something(models.Model):
foo = CITextField()
Django also provides CIEmailField and CICharField, which are case-insensitive versions of EmailField and CharField.
Store the original mixed-case string in a plain text column. Use the data type text or varchar without length modifier rather than varchar(n). They are essentially the same, but with varchar(n) you have to set an arbitrary length limit, that can be a pain if you want to change later. Read more about that in the manual or in this related answer by Peter Eisentraut #serverfault.SE.
Create a functional unique index on lower(string). That's the major point here:
CREATE UNIQUE INDEX my_idx ON mytbl(lower(name));
If you try to INSERT a mixed case name that's already there in lower case you get a unique key violation error.
For fast equality searches use a query like this:
SELECT * FROM mytbl WHERE lower(name) = 'foo' --'foo' is lower case, of course.
Use the same expression you have in the index (so the query planner recognizes the compatibility) and this will be very fast.
As an aside: you may want to upgrade to a more recent version of PostgreSQL. There have been lots of important fixes since 8.4.2. More on the official Postgres versioning site.
With overriding the model manager, you have two options. First is to just create a new lookup method:
class MyModelManager(models.Manager):
def get_by_username(self, username):
return self.get(username__iexact=username)
class MyModel(models.Model):
...
objects = MyModelManager()
Then, you use get_by_username('blah') instead of get(username='blah'), and you don't have to worry about forgetting iexact. Of course that then requires that you remember to use get_by_username.
The second option is much hackier and convoluted. I'm hesitant to even suggest it, but for completeness sake, I will: override filter and get such that if you forget iexact when querying by username, it will add it for you.
class MyModelManager(models.Manager):
def filter(self, **kwargs):
if 'username' in kwargs:
kwargs['username__iexact'] = kwargs['username']
del kwargs['username']
return super(MyModelManager, self).filter(**kwargs)
def get(self, **kwargs):
if 'username' in kwargs:
kwargs['username__iexact'] = kwargs['username']
del kwargs['username']
return super(MyModelManager, self).get(**kwargs)
class MyModel(models.Model):
...
objects = MyModelManager()
As of December 2021, with the help of Django 4.0 UniqueConstraint expressions you can add a Meta class to your model like this:
class Meta:
constraints = [
models.UniqueConstraint(
Lower('<field name>'),
name='<constraint name>'
),
]
I'm by no mean a Django professional developer and I don't know technical considerations like performance issues about this solution. Hope others comment on that.
Since a username is always lowercase, it's recommended to use a custom lowercase model field in Django. For the ease of access and code-tidiness, create a new file fields.py in your app folder.
from django.db import models
from django.utils.six import with_metaclass
# Custom lowecase CharField
class LowerCharField(with_metaclass(models.SubfieldBase, models.CharField)):
def __init__(self, *args, **kwargs):
self.is_lowercase = kwargs.pop('lowercase', False)
super(LowerCharField, self).__init__(*args, **kwargs)
def get_prep_value(self, value):
value = super(LowerCharField, self).get_prep_value(value)
if self.is_lowercase:
return value.lower()
return value
Usage in models.py
from django.db import models
from your_app_name.fields import LowerCharField
class TheUser(models.Model):
username = LowerCharField(max_length=128, lowercase=True, null=False, unique=True)
End Note : You can use this method to store lowercase values in the database, and not worry about __iexact.
You can use citext postgres type instead and not bother anymore with any sort of iexact. Just make a note in model that underlying field is case insensitive.
Much easier solution.
You can use lookup='iexact' in UniqueValidator on serializer, like this:
Unique model field in Django and case sensitivity (postgres)
I liked Chris Pratt's Answer but it didn't worked for me, because the models.Manager-class doesn't have the get(...) or filter(...) Methods.
I had to take an extra step via a custom QuerySet:
from django.contrib.auth.base_user import BaseUserManager
from django.db.models import QuerySet
class CustomUserManager(BaseUserManager):
# Use the custom QuerySet where get and filter will change 'email'
def get_queryset(self):
return UserQuerySet(self.model, using=self._db)
def create_user(self, email, password, **extra_fields):
...
def create_superuser(self, email, password, **extra_fields):
...
class UserQuerySet(QuerySet):
def filter(self, *args, **kwargs):
if 'email' in kwargs:
# Probably also have to replace...
# email_contains -> email_icontains,
# email_exact -> email_iexact,
# etc.
kwargs['email__iexact'] = kwargs['email']
del kwargs['email']
return super().filter(*args, **kwargs)
def get(self, *args, **kwargs):
if 'email' in kwargs:
kwargs['email__iexact'] = kwargs['email']
del kwargs['email']
return super().get(*args, **kwargs)
This worked for me in a very simple case but is working pretty good so far.
You can also override get_prep_value() and reuse it through inheritance.
class LowerCaseField:
def get_prep_value(self, value):
value = super().get_prep_value(value)
if value:
value = value.strip().lower()
return value
class LowerSlugField(LowerCaseField, models.SlugField):
pass
class LowerEmailField(LowerCaseField, models.EmailField):
pass
class MyModel(models.Model):
email = LowerEmailField(max_length=255, unique=True)
This way, if you ever want to reuse this field in another model, you can use the same consistent strategy.
From Django Docs:
get_prep_value(value)
value is the current value of the model’s
attribute, and the method should return data in a format that has been
prepared for use as a parameter in a query.
See Converting Python objects to query values for usage.

Django: multiple models in one template using forms [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm building a support ticket tracking app and have a few models I'd like to create from one page. Tickets belong to a Customer via a ForeignKey. Notes belong to Tickets via a ForeignKey as well. I'd like to have the option of selecting a Customer (that's a whole separate project) OR creating a new Customer, then creating a Ticket and finally creating a Note assigned to the new ticket.
Since I'm fairly new to Django, I tend to work iteratively, trying out new features each time. I've played with ModelForms but I want to hide some of the fields and do some complex validation. It seems like the level of control I'm looking for either requires formsets or doing everything by hand, complete with a tedious, hand-coded template page, which I'm trying to avoid.
Is there some lovely feature I'm missing? Does someone have a good reference or example for using formsets? I spent a whole weekend on the API docs for them and I'm still clueless. Is it a design issue if I break down and hand-code everything?
This really isn't too hard to implement with ModelForms. So lets say you have Forms A, B, and C. You print out each of the forms and the page and now you need to handle the POST.
if request.POST():
a_valid = formA.is_valid()
b_valid = formB.is_valid()
c_valid = formC.is_valid()
# we do this since 'and' short circuits and we want to check to whole page for form errors
if a_valid and b_valid and c_valid:
a = formA.save()
b = formB.save(commit=False)
c = formC.save(commit=False)
b.foreignkeytoA = a
b.save()
c.foreignkeytoB = b
c.save()
Here are the docs for custom validation.
I just was in about the same situation a day ago, and here are my 2 cents:
1) I found arguably the shortest and most concise demonstration of multiple model entry in single form here: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .
In a nutshell: Make a form for each model, submit them both to template in a single <form>, using prefix keyarg and have the view handle validation. If there is dependency, just make sure you save the "parent"
model before dependant, and use parent's ID for foreign key before commiting save of "child" model. The link has the demo.
2) Maybe formsets can be beaten into doing this, but as far as I delved in, formsets are primarily for entering multiples of the same model, which may be optionally tied to another model/models by foreign keys. However, there seem to be no default option for entering more than one model's data and that's not what formset seems to be meant for.
I very recently had the some problem and just figured out how to do this.
Assuming you have three classes, Primary, B, C and that B,C have a foreign key to primary
class PrimaryForm(ModelForm):
class Meta:
model = Primary
class BForm(ModelForm):
class Meta:
model = B
exclude = ('primary',)
class CForm(ModelForm):
class Meta:
model = C
exclude = ('primary',)
def generateView(request):
if request.method == 'POST': # If the form has been submitted...
primary_form = PrimaryForm(request.POST, prefix = "primary")
b_form = BForm(request.POST, prefix = "b")
c_form = CForm(request.POST, prefix = "c")
if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
print "all validation passed"
primary = primary_form.save()
b_form.cleaned_data["primary"] = primary
b = b_form.save()
c_form.cleaned_data["primary"] = primary
c = c_form.save()
return HttpResponseRedirect("/viewer/%s/" % (primary.name))
else:
print "failed"
else:
primary_form = PrimaryForm(prefix = "primary")
b_form = BForm(prefix = "b")
c_form = Form(prefix = "c")
return render_to_response('multi_model.html', {
'primary_form': primary_form,
'b_form': b_form,
'c_form': c_form,
})
This method should allow you to do whatever validation you require, as well as generating all three objects on the same page. I have also used javascript and hidden fields to allow the generation of multiple B,C objects on the same page.
The MultiModelForm from django-betterforms is a convenient wrapper to do what is described in Gnudiff's answer. It wraps regular ModelForms in a single class which is transparently (at least for basic usage) used as a single form. I've copied an example from their docs below.
# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile
User = get_user_model()
class UserEditForm(forms.ModelForm):
class Meta:
fields = ('email',)
class UserProfileForm(forms.ModelForm):
class Meta:
fields = ('favorite_color',)
class UserEditMultiForm(MultiModelForm):
form_classes = {
'user': UserEditForm,
'profile': UserProfileForm,
}
# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm
User = get_user_model()
class UserSignupView(UpdateView):
model = User
form_class = UserEditMultiForm
success_url = reverse_lazy('home')
def get_form_kwargs(self):
kwargs = super(UserSignupView, self).get_form_kwargs()
kwargs.update(instance={
'user': self.object,
'profile': self.object.profile,
})
return kwargs
I currently have a workaround functional (it passes my unit tests). It is a good solution to my opinion when you only want to add a limited number of fields from other models.
Am I missing something here ?
class UserProfileForm(ModelForm):
def __init__(self, instance=None, *args, **kwargs):
# Add these fields from the user object
_fields = ('first_name', 'last_name', 'email',)
# Retrieve initial (current) data from the user object
_initial = model_to_dict(instance.user, _fields) if instance is not None else {}
# Pass the initial data to the base
super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
# Retrieve the fields from the user model and update the fields with it
self.fields.update(fields_for_model(User, _fields))
class Meta:
model = UserProfile
exclude = ('user',)
def save(self, *args, **kwargs):
u = self.instance.user
u.first_name = self.cleaned_data['first_name']
u.last_name = self.cleaned_data['last_name']
u.email = self.cleaned_data['email']
u.save()
profile = super(UserProfileForm, self).save(*args,**kwargs)
return profile
"I want to hide some of the fields and do some complex validation."
I start with the built-in admin interface.
Build the ModelForm to show the desired fields.
Extend the Form with the validation rules within the form. Usually this is a clean method.
Be sure this part works reasonably well.
Once this is done, you can move away from the built-in admin interface.
Then you can fool around with multiple, partially related forms on a single web page. This is a bunch of template stuff to present all the forms on a single page.
Then you have to write the view function to read and validated the various form things and do the various object saves().
"Is it a design issue if I break down and hand-code everything?" No, it's just a lot of time for not much benefit.
According to Django documentation, inline formsets are for this purpose:
"Inline formsets is a small abstraction layer on top of model formsets. These simplify the case of working with related objects via a foreign key".
See https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

Categories

Resources