I, too, struggle with this frequently.
On Wednesday, July 2, 2014 9:07:04 PM UTC-6, Jon Dufresne wrote:
>
> I'm reporting this to the developers list as I feel this shows a
> shortfall in (to me) expected behavior. I'm not sure this is exactly a
> bug or simply a use case the unique validation wasn't designed for.
>
> Basically, sometimes I want to create model formsets that use a
> limited number of model fields. These model fields may have a unique
> constraint using unique_together. The other field (or fields) of
> unique together may not be included in the formset. Upon validating
> the form, unique_together fields are only checked if all fields are
> contained in the form. This means, my unique fields will not be
> validated automatically. To achieve the validation I usually have to
> copy parts of Django form validation, missing out on DRY.
>
> I think one solution would be for model formsets to have a "static" or
> "constant" fields argument. This could be a single value per field
> that all forms in the formset would share for a particular set of
> fields. These fields would not be a part of the rendered form, but
> could be used for validating the forms and creating new models
> instances. Thoughts?
>
> An example where I might do this: suppose I have a "container" model.
> This model has many "item" models created through a FK relationship.
> Perhaps a field is unique together with the container. This example
> could apply to any container/item relationship. I might create a
> formset for all items of just one container for a bulk (unique)
> rename. I have created a simple small example that illustrates my
> point:
>
> models.py
>
> ----
> class Container(models.Model):
> pass
>
> class Item(models.Model):
> container = models.ForeignKey(Container)
> name = models.CharField(max_length=100)
>
> class Meta:
> unique_together = ('container', 'name')
>
> ItemFormSet = modelformset_factory(model=Item, fields=['name'])
> ----
>
> tests.py
> ----
> class ItemFormSetTestCase(TestCase):
> def test_unique_item_name(self):
> container = Container()
> container.save()
> item1 = Item(container=container, name='item1')
> item1.save()
> item2 = Item(container=container, name='item2')
> item2.save()
> data = {
> 'form-TOTAL_FORMS': 2,
> 'form-INITIAL_FORMS': 2,
> 'form-MAX_NUM_FORMS': 2,
> 'form-0-id': str(item1.pk),
> 'form-0-name': 'newname',
> 'form-1-id': str(item2.pk),
> 'form-1-name': 'newname',
> }
> formset = ItemFormSet(
> data,
> queryset=Item.objects.filter(container=container))
> self.assertFalse(formset.is_valid())
> ---
>
> This test fails because the uniqueness of name is not actually
> validated. If I were to go ahead an save this "valid" form, I receive
> the following error:
>
> ---
> Traceback (most recent call last):
> File "/home/jon/djtest/djtest/myapp/tests.py", line 27, in
> test_unique_item_name
> formset.save()
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/forms/models.py",
> line 636, in save
> return self.save_existing_objects(commit) +
> self.save_new_objects(commit)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/forms/models.py",
> line 753, in save_existing_objects
> saved_instances.append(self.save_existing(form, obj, commit=commit))
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/forms/models.py",
> line 623, in save_existing
> return form.save(commit=commit)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/forms/models.py",
> line 457, in save
> construct=False)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/forms/models.py",
> line 103, in save_instance
> instance.save()
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/base.py",
>
> line 590, in save
> force_update=force_update, update_fields=update_fields)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/base.py",
>
> line 618, in save_base
> updated = self._save_table(raw, cls, force_insert, force_update,
> using, update_fields)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/base.py",
>
> line 680, in _save_table
> forced_update)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/base.py",
>
> line 724, in _do_update
> return filtered._update(values) > 0
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/query.py",
>
>
> line 598, in _update
> return query.get_compiler(self.db).execute_sql(CURSOR)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/sql/compiler.py",
>
>
> line 1003, in execute_sql
> cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/models/sql/compiler.py",
>
>
> line 785, in execute_sql
> cursor.execute(sql, params)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/backends/utils.py",
>
>
> line 65, in execute
> return self.cursor.execute(sql, params)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/utils.py",
> line 94, in __exit__
> six.reraise(dj_exc_type, dj_exc_value, traceback)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/backends/utils.py",
>
>
> line 65, in execute
> return self.cursor.execute(sql, params)
> File
> "/home/jon/djtest/venv/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py",
>
>
> line 485, in execute
> return Database.Cursor.execute(self, query, params)
> IntegrityError: UNIQUE constraint failed: myapp_item.container_id,
> myapp_item.name
> ---
>
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/0e2d6c3d-2b92-4b4f-9841-e8661cba3af2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.