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.

Reply via email to