2

Consider I have defined the following models:

class Tag(models.Model):
  name = models.CharField(max_length=20)

class Entry(models.Model):
  title = models.CharField(max_length=100)
  date = models.DateField()
  tags = models.ManyToManyField(Tag)

and the following model form:

class EntryForm(forms.ModelForm):
  tags =  CharField(max_length=100, label='Tags', required=False)
  class Meta:
    model = Entry

Basically I have an entry with tags. The tags are not necessarily already created. I need to receive a filled entry form and save it with tags.

I can do that easily if I don't have the ManyToMany relation in the Entry. But with it I first need to save the tags, then add the saved tags to the entry and then save the entry. Which means I have to override the EntryForm's save() method.

I saw this question already, but it looks different. In my case I need to save only the tags, and the rest should be able use ModelForm's default save().

I presented a simplified example. In real one I have much more fields in the Entry. What is your recommendation for writing EntryForm's save() method in a django way?

Community
  • 1
  • 1
grigy
  • 6,696
  • 12
  • 49
  • 76

2 Answers2

3

I think you can call super after you save the tags. Perhaps you need to append to the entry after the entry is created.

def save(self, commit=True):
    # do custom stuff
    tags =[]
    for t in self.cleaned_data['tags'].split(','):
         tags.append(Tag.objects.get_or_create(name=t))
    # save entry
    e = super(EntryForm, self).save(commit=commit)

    for t in tags:
        e.tags.add(t)
OmerGertel
  • 2,573
  • 1
  • 19
  • 27
  • Thanks. But one problem. How can I prevent initial saving of tags field at super(EntryForm, self).save()? – grigy Oct 20 '10 at 15:46
  • 1
    Good question. You can also save the Entry manually. If you get the instance, `Entry.objects.get(id=instance.id)` and then update the fields, otherwise `Entry.objects.create(title=...,date=...)`. Then add the tags. It's so little work that using the form's save may not be worth it. – OmerGertel Oct 20 '10 at 16:52
  • One correction: get_or_create returns a tuple - (tag, created), so we need to take only the first part – grigy Oct 21 '10 at 19:03
1

The best and correct option here would be to add the clean_tags method to the form, not to override the save() in the model.

def clean_tags(self):
    tags = self.cleaned_data.get('tags', None)

    clean_tags = []


    tags = [tag.strip() for tag in tags.split(',') if tags]

    for tag in tags:
        t, created = Tag.objects.get_or_create(title=tag)
        t.count = F('count') + 1
        t.user.add(self.cleaned_data['user'])
        t.save()
        clean_tags.append(t)

    return clean_tags
Pavel Shvedov
  • 1,284
  • 11
  • 8