Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 1.8.3 - model field validation with related object

For the following set of models (Foo, Bar), you could impose a cross-validation rule like the one in the Bar.clean of the following code snippet up to django 1.7.

The same snippet throws a RelatedObjectDoesNotExist error in django 1.8.3.

What is the new and improved way of achieving the same result in django 1.8.3?

(I've included the admin.py code just to show how these models are being used.)

models.py

from django.db import models
from django.core.exceptions import ValidationError

class Foo(models.Model):
   name = models.CharField("Name", blank=True, max_length=300)

class Bar(models.Model):
   name = models.CharField("Name", blank=True, max_length=300)
   foo = models.ForeignKey('Foo', verbose_name='Foo')

   def clean(self):
      if self.name + self.foo.name != 'FooBar':
         raise ValidationError('Concatenation should be FooBar.')

admin.py

from django.contrib import admin
import models

class BarInline(admin.TabularInline):
    model = models.Bar

class FooAdmin(admin.ModelAdmin):
    model = models.Foo
    inlines = [BarInline,]

site = admin.site
site.register(models.Foo,FooAdmin)
like image 741
Khodeir Avatar asked Jul 12 '15 19:07

Khodeir


1 Answers

I've added a simple output modification to your code

def clean(self):
  print(self.__dict__)

  if self.name + self.foo.name != 'FooBar':
     raise ValidationError('Concatenation should be FooBar.')

The simple print statements will print out Bar object before executing of main code.

Now I've tested the code with Django 1.8.x and I've got the exception as you mentioned this is the result:

{'_state': <django.db.models.base.ModelState object at 0x7ff55cd30710>, 'id': None, 'foo_id': None, 'name': 'Bar 1'}

Now I've tested it again with Django 1.7.x and it works ok, the output result is:

{'_foo_cache': <Foo: Foo object>, 'name': 'Bar 1', 'id': None, 'foo_id': None, '_state': <django.db.models.base.ModelState object at 0x7f731151c9e8>}

As you may noticed the foo_id in both cases in None but what does the magic is the _foo_cache thing that was removed in Django 1.8

The substitute that I can suggest you is to move your validation into Forms

Made these changes : admin.py

class BarInline(admin.TabularInline):
    model = Bar
    form = BarForm

forms.py

class BarForm(forms.models.ModelForm):
    class Meta:
        model = Bar
        fields = ('name',)

    def clean(self):
      data = self.cleaned_data     
      if not data['name'] + data['foo'].name == "foobar":
         raise ValidationError('Concatenation should be FooBar.')
like image 69
Serjik Avatar answered Oct 03 '22 05:10

Serjik