views:

359

answers:

3

2 questions:

  • How can I stop duplicates from being created when parent=None and name is the same?
  • Can i call a model method from within the form?

Please see full details below:

models.py

class MyTest(models.Model):
    parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
    name = models.CharField(max_length=50)
    slug = models.SlugField(max_length=255, blank=True, unique=True)
    owner = models.ForeignKey(User, null=True)

    class Meta:
        unique_together = ("parent", "name")

    def save(self, *args, **kwargs):
        self.slug = self.make_slug() 
        super(MyTest, self).save(*args, **kwargs)

    def make_slug(self):
        # some stuff here 
        return generated_slug

note: slug = unique as well!

forms.py

class MyTestForm(forms.ModelForm):
    class Meta:
        model = MyTest
        exclude = ('slug',)

    def clean_name(self):
        name = self.cleaned_data.get("name")
        parent = self.cleaned_data.get("parent")

        if parent is None:
            # this doesn't work when MODIFYING existing elements!
            if len(MyTest.objects.filter(name = name, parent = None)) > 0:
                raise forms.ValidationError("name not unique")
        return name

Details

The unique_together contraint works perfectly w/ the form when parent != None. However when parent == None (null) it allows duplicates to be created.

In order to try and avoid this, i tried using the form and defined clean_name to attempt to check for duplicates. This works when creating new objects, but doesn't work when modifying existing objects.

Someone had mentioned i should use commit=False on the ModelForm's .save, but I couldn't figure out how to do/implement this. I also thought about using the ModelForm's has_changed to detect changes to a model and allow them, but has_changed returns true on newly created objects with the form as well. help!

Also, (somewhat a completely different question) can I access the make_slug() model method from the Form? I believe that currently my exclude = ('slug',) line is also ignoring the 'unique' constraint on the slug field, and in the models save field, I'm generating the slug instead. I was wondering if i could do this in the forms.py instead?

A: 

I don't know for sure this will fix your problem, but I suggest testing your code on the latest Django trunk code. Get it with:

svn co http://code.djangoproject.com/svn/django/trunk/

There have been several fixes to unique_together since the release of 1.02, for example see ticket 9493.

Van Gale
im already running trunk
lostincode
A: 

Unique together should be a tuple of tuples

unique_together = (("parent", "name"),)
phillc
this is no longer needed
lostincode
A: 

You could have a different form whether you are creating or updating.

Use the instance kwarg when instantiating the form.

if slug:
    instance = MyTest.object.get( slug=slug )
    form = MyUpdateTestForm( instance=instance )
else:
    form = MyTestForm()

For the second part, I think that's where you could bring in commit=False, something like:

if form.is_valid():
    inst = form.save( commit=False )
    inst.slug = inst.make_slug()
    inst.save()
skyl