2010-10-05 12 views
7

Usando el siguiente código:error utilizando un campo de clase base en la subclase unique_together opción meta

class Organization(models.Model): 
    name = models.CharField(max_length="100",) 
    alias = models.SlugField() 
    ... 

class Division(Organization): 
    parent_org = models.ForeignKey(Organization) 

    class Meta: 
     unique_together=['parent_org', 'alias'] 
     ... 

Tratando de syncdb dame este error:

Error: One or more models did not validate: 
organizations.division: "unique_together" refers to alias. This is not in the 
same model as the unique_together statement. 

se aprecia Cualquier ayuda,

Gracias,

Eric

+0

Puede explicar el requisito más, no es capaz de entender lo que es la necesidad de heredar la Organización y tener ForeignKey con el mismo modelo de base. –

+0

Es una simple relación padre-hijo. Una organización puede tener múltiples Divisiones, una División es de algún tipo una Organización especializada. –

Respuesta

12

Esto es por diseño. La lectura de la documentación para la opción unique_together, se establece que:

It's used in the Django admin and is enforced at the database level.

Si nos fijamos en la tabla que se crea una subclase, verá que no realidad tienen los campos que su padre tiene. En su lugar, obtiene una clave externa suave para la tabla primaria con un nombre de campo llamado [field]_ptr_id, donde [field] es el nombre de la tabla que está heredando al excluir el nombre de la aplicación. Entonces su tabla de división tiene una clave externa primaria llamada organization_ptr_id.

Ahora porque unique_together se aplica a nivel de la base de datos utilizando la restricción UNIQUE, no hay forma de que sepa que la base de datos realmente la aplica a un campo que no está en la tabla.

Su mejor opción es probablemente mediante el uso de Validators en su nivel de lógica empresarial, o volver a pensar en el esquema de base de datos para soportar la restricción.

Editar: Como Manoj señaló, también podría intentar usar Model Validators como validate_unique.

+1

+1 por mencionar los motivos y vincular a los validadores. Es posible que desee señalar _model validations_ también. Por ejemplo 'validate_unique': http: // docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs#django.db.models.Model.validate_unique –

4

[Modelo] Los validadores funcionarían para usted. Tal vez más simple, sin embargo, sería utilizar:

class BaseOrganization(models.Model): 
    name = models.CharField(max_length="100",) 
    alias = models.SlugField() 
    class Meta: 
     abstract = True 

class Organization(BaseOrganization): 
    pass 

class Division(BaseOrganization): 
    parent_org = models.ForeignKey(Organization) 

    class Meta: 
     unique_together=['parent_org', 'alias'] 

Nota: Al igual que con el código actual, no podría tener subdivisiones de las divisiones.

1

Esta es una solución recientemente he usado en Django 1.6 (gracias a Manoj Govindan de la idea):

class Organization(models.Model): 
    name = models.CharField(max_length="100",) 
    alias = models.SlugField() 
    ... 

class Division(Organization): 
    parent_org = models.ForeignKey(Organization) 

    # override Model.validate_unique 
    def validate_unique(self, exclude=None):  
     # these next 5 lines are directly from the Model.validate_unique source code 
     unique_checks, date_checks = self._get_unique_checks(exclude=exclude) 
     errors = self._perform_unique_checks(unique_checks) 
     date_errors = self._perform_date_checks(date_checks) 
     for k, v in date_errors.items(): 
      errors.setdefault(k, []).extend(v) 

     # here I get a list of all pairs of parent_org, alias from the database (returned 
     # as a list of tuples) & check for a match, in which case you add a non-field 
     # error to the error list 
     pairs = Division.objects.exclude(pk=self.pk).values_list('parent_org', 'alias') 
     if (self.parent_org, self.alias) in pairs: 
       errors.setdefault(NON_FIELD_ERRORS, []).append('parent_org and alias must be unique') 

     # finally you raise the ValidationError that includes all validation errors, 
     # including your new unique constraint 
     if errors: 
      raise ValidationError(errors) 
+0

Obviamente, esto no implica ningún control de nivel de la base de datos, ya que una restricción única dentro de una tabla lo haría, pero en términos de validación de Django, esta solución funcionó perfectamente para mí. El único que tuve que arreglar fue asegurarse de excluir la instancia que llama validate_unique (por lo tanto, excluir (pk = self.pk)). –

2

Esto no se aplica estrictamente la pregunta, pero está muy estrechamente relacionado; unique_together funcionará si la clase base es abstracta. Puede marcar las clases del modelo abstractas como tales usando:

class Meta(): 
    abstract = True 

Esto evitará Django desde la creación de una mesa para la clase, y sus campos se incluirá directamente en cualquier subclase. En esta situación, unique_together es posible porque todos los campos están en la misma tabla.

https://docs.djangoproject.com/en/1.5/topics/db/models/#abstract-base-classes

Cuestiones relacionadas