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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
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.

Reply via email to