Model Forms: Clean unique field

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from django import forms

def clean_unique(form, field, exclude_initial=True, 
                 format="The %(field)s %(value)s has already been taken."):
    value = form.cleaned_data.get(field)
    if value:
        qs = form._meta.model._default_manager.filter(**{field:value})
        if exclude_initial and form.initial:
            initial_value = form.initial.get(field)
            qs = qs.exclude(**{field:initial_value})
        if qs.count() > 0:
            raise forms.ValidationError(format % {'field':field, 'value':value})
    return value

# Usage:

class DeployForm(forms.ModelForm):
    """We want both the slug and cname fields to be unique"""

    class Meta:
        model = Website
        fields = ['slug', 'cname']
        
    def clean_slug(self):
        return clean_unique(self, 'slug')
        
    def clean_cname(self):
        return clean_unique(self, 'cname') 

More like this

  1. Getting the global error of a form by baptiste 6 years, 1 month ago
  2. Automate unique slugs by taojian 5 years, 6 months ago
  3. ingore_fields by RealNitro 5 years, 7 months ago
  4. Unique Slugify by SmileyChris 5 years, 2 months ago
  5. check if form has changed (deprecated) by guettli 5 years, 3 months ago

Comments

fnl (on December 4, 2008):

Django actually supports this problem if you know how, ie the snippet might not really be the right solution:

class MyModel(models.Model):
    email = models.EmailField(unique=True)

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

def my_model_view(request, model_id):
    obj = get_object_or_404(MyModel, pk=model_id)
    if request.method == 'POST':
        form = MyModelForm(request.POST, instance=obj)
        if form.is_valid():
            obj = form.save()
    else:
        form = MyModelForm(instance=obj)
    render_to_response(
        'my_model_template.html', {'form': form},
        context_instance=RequestContext(request)
    )

The whole trick is using the instance parameter when creating the form instance from the post. django will load and know about the unique field, ie the line:

form = MyModelForm(request.POST, instance=obj)

is essential.

#

johnboxall (on December 4, 2008):

Whoa, you're right - not sure how I missed that!

#

duh386 (on February 19, 2013):

We can try a little easily method.

class MyModel(models.Model):
    email = models.EmailField(unique=True)

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        exclude = ('email',)
    email = models.CharField(max_length=40)

On this way we must redefine email-field at all, but we don't need to override init method. This may be useful for remove validation for 'unique' attribute for one or few field (not for all).

#

(Forgotten your password?)