Método clean() y comprobar si se quiere actualizar o insertar

189 views
Skip to first unread message

Javi

unread,
Nov 19, 2010, 7:38:30 AM11/19/10
to Django-es
Hola lista,

desarrollo una aplicación en la que existe un modelo categorías tal
que así:
class Category(models):
...
slug = models.SlugField(_('Slug'), max_length=255,
help_text=_('Unique value for category page URL, created from name.'))
parent = models.ForeignKey('self', verbose_name=_('Parent'),
blank=True, null=True, related_name='children')
...

class Meta:
...
unique_together = (("slug", "parent"),)

de modo que pretendo que varias categorías puedan tener el mismo slug
salvo que ya exista ese slug para ese parent, ejemplo:

category.slug = 'rojo', category.parent = 'coche'
category.slug = 'rojo', category.parent = 'moto'
OK

category.slug = 'rojo', category.parent = 'coche'
category.slug = 'rojo', category.parent = 'coche'
MAL

Hasta ahí todo bien, el problema es que por cuestiones de
funcionamiento interno de Django (1.2.3) y Mysql (5.1 creo recordar)
unique_together no comprueba el caso en el que parent es None, que es
cuando la base de datos tiene ese campo a Null y permite cosas como:

category.slug = 'rojo', category.parent = None
category.slug = 'rojo', category.parent = None
OK -> debería estar MAL

Así que he definido un formulario con un método clean_slug() de la
siguiente forma:
class CategoryAdminForm(forms.ModelForm):
"""
Formulario para comprobar que no se intentan guardar dos
categorías en el mismo nivel/con mismo padre
"""
def clean_slug(self):
#comprobar si parent == None que self.slug no existe ya
#unique_together(("slug", "parent"),) no tiene en cuenta que
parent sea None :-(

data = self.cleaned_data['slug']
parent = getattr(self.data, 'parent', None) # tomamos el
atributo parent y lo ponemos a None si es vacío para poder compararlo
categories = Category.objects.filter(slug=data, parent=parent)

# el problema es que unique_together no funciona para parent =
None ¿no?
# pues entonces vamos a comprobar solo para esos casos
if parent == None:
categories = Category.objects.filter(slug=data, parent=parent)
if categories.exists():
raise forms.ValidationError(_(u'Ya existe una categoría con
esta Categoría superior y esta Ruta url'))

return data

Esto funciona, pero tiene un inconveniente: no permite actualizar
objetos existentes ya que detecta que ya existe uno con el mismo slug
y parent :-(.

¿Alguna idea de cómo detectar en un método clean si se está
actualizando o insertando?
Sé que esto se puede hacer al sobreescribir el método save() del
modelo, pero entonces no podría notificar al usuario del problema en
el formulario de administración.
En clean_slug(self) no tengo acceso al request que creo que es lo que
necesito para distinguir un caso de otro.

Espero que a alguien se le encienda la lucecita, un saludo,
Javi.

Javi

unread,
Nov 19, 2010, 7:49:23 AM11/19/10
to Django-es
Bah, odio cuando me pasa esto, medio día buscando y justo después de
preguntarlo públicamente encuentro lo que buscaba...

Para todo el que tenga un problema similar, la solución está en usar
self.initial, que recibe un diccionario con los contenidos que había
antes si es que se está editando un objeto o un diccionario vacío si
se está creando uno nuevo.

¡Un saludo a todos!
Javi

class CategoryAdminForm(forms.ModelForm):
"""
Formulario para comprobar que no se intentan guardar dos
categorías en el mismo nivel/con mismo padre
"""
def clean_slug(self):
#comprobar si parent == None que self.slug no existe ya
#unique_together(("slug", "parent"),) no tiene en cuenta que
parent sea None :-(

data = self.cleaned_data['slug']
parent = getattr(self.data, 'parent', None) # tomamos el
atributo parent y lo ponemos a None si es vacío para poder compararlo

# el problema es que unique_together no funciona para parent =
None ¿no?
# pues entonces vamos a comprobar solo para esos casos :D
if parent == None:
categories = Category.objects.filter(slug=data, parent=parent)
if categories.exists() and not self.initial: # no estamos editando
raise forms.ValidationError(_(u'Ya existe una categoría con
esta Categoría superior y esta Ruta url'))

return data

Reply all
Reply to author
Forward
0 new messages