Вопрос

I have the following models:

class Recipe(models.Model):
    fields...

class Ingredient(models.Model):
    fields...

class UsesIngredient(models.Model):
    recipe = models.ForeignKey(Recipe)
    ingredient = models.ForeignKey(Ingredient)
    amount = models.FloatField()
    group = models.CharField()

I have a view which lets the user add any number of 'UsesIngredient' models for a certain recipe through a dynamic formset. The group attribute is automatically filled in an hidden from the user.

The problem is that when the users adds a new form in the formset, but doesn't fill in any of the fields, I don't want that form saved. However, django still tries to save the form because the 'group' attribute has 'changed' (because it has been automatically filled in when the extra form was created).

Is there any way to get around this?

Thanks!

Это было полезно?

Решение

Well, I still didn't feel completely comfortable with Tim Edgar's solution, so I kept looking. I guess I found what I was looking for. The 'Form' class, has two undocumented methods that are of use in this case: 'has_changed()' and '_get_changed_data'.

During ModelFormSet validation, every form checks 'has_changed()'. If the form did not changed, validation is skipped and a correct form is assumed. Likewise, during ModelFormSet saving, the save_new_objects checks every form to see if it has changed. If it didn't change, the form isn't saved.

So my solution was to override the has_changed() method to return False if only the 'group' attribute has changed, and all other fields are empty. This is my implementation:

class UsesIngredientForm(forms.ModelForm):    
    class Meta:
        model = UsesIngredient

    def has_changed(self, *args, **kwargs):
        self._get_changed_data(*args, **kwargs)
        # If group is in changed_data, but no other fields are filled in, remove group so
        # the form will not be validated or saved
        if 'group' in self._changed_data and len(self._changed_data) == 1:
            contains_data = False
            for name in ['ingredient', 'amount', 'unit']:
                field = self.fields[name]
                prefixed_name = self.add_prefix(name)
                data_value = field.widget.value_from_datadict(self.data, self.files, prefixed_name)
                if data_value:
                    contains_data = True
                    break
            if not contains_data:
                self._changed_data.remove('group')
        return bool(self._changed_data)

Hope this helps anybody in the future!

EDIT: I edited this answer to reflect Tim Edgars comment. I realize that this implementation still uses 'private' methods, but I haven't found a cleaner implementation using just the publicly documented methods. But then maybe that is just my own incompetence :).

Другие советы

You could try making all your fields to require a value by setting blank=False. See more here. It should require validation that the values that you care about are not left blank.

If that doesn't work, you can try creating your own custom save method that does the validation that you care about.

def save(self, *args, **kwargs):
   # Do your checks on the properties such as self.group, self.amount, etc
   # If it is fine then call
   super(UsesIngredient, self).save(*args, **kwargs)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top