topics makemessages i18n example docs djangoproject python django django-models django-managers

python - makemessages - django translation



Django Manager encadenamiento (4)

Me preguntaba si era posible (y, en caso afirmativo, cómo) encadenar a varios administradores para producir un conjunto de consultas que se ve afectado por ambos administradores individuales. Explicaré el ejemplo específico en el que estoy trabajando:

Tengo múltiples clases de modelos abstractos que utilizo para proporcionar funcionalidad pequeña y específica a otros modelos. Dos de estos modelos son DeleteMixin y GlobalMixin.

DeleteMixin se define como tal:

class DeleteMixin(models.Model): deleted = models.BooleanField(default=False) objects = DeleteManager() class Meta: abstract = True def delete(self): self.deleted = True self.save()

Básicamente proporciona una pseudo-eliminación (la bandera eliminada) en lugar de eliminar realmente el objeto.

GlobalMixin se define como tal:

class GlobalMixin(models.Model): is_global = models.BooleanField(default=True) objects = GlobalManager() class Meta: abstract = True

Permite que cualquier objeto se defina como un objeto global o como un objeto privado (como una publicación de blog pública / privada).

Ambos tienen sus propios administradores que afectan el conjunto de consulta que se devuelve. Mi DeleteManager filtra el conjunto de consulta para que solo devuelva resultados que tienen el marcador eliminado como False, mientras que GlobalManager filtra el conjunto de consulta para que solo devuelva resultados marcados como globales. Aquí está la declaración para ambos:

class DeleteManager(models.Manager): def get_query_set(self): return super(DeleteManager, self).get_query_set().filter(deleted=False) class GlobalManager(models.Manager): def globals(self): return self.get_query_set().filter(is_global=1)

La funcionalidad deseada sería hacer que un modelo extienda ambos modelos abstractos y otorgar la capacidad de solo devolver los resultados que no son eliminados y globales. Ejecuté un caso de prueba en un modelo con 4 instancias: una fue global y no eliminada, una fue global y eliminada, una no global y no eliminada, y una no global y eliminada. Si trato de obtener conjuntos de resultados como tales: SomeModel.objects.all (), obtengo la instancia 1 y 3 (las dos no eliminadas, ¡genial!). Si pruebo SomeModel.objects.globals (), aparece el error de que DeleteManager no tiene un carácter global (suponiendo que mi declaración de modelo sea como tal: SomeModel (DeleteMixin, GlobalMixin). Si invierto el orden, no lo hago. t recibe el error, pero no filtra los eliminados). Si cambio GlobalMixin para adjuntar GlobalManager a globales en lugar de objetos (por lo que el nuevo comando sería SomeModel.globals.globals ()), obtengo las instancias 1 y 2 (las dos globales), mientras que mi resultado previsto sería obtener solo una instancia 1 (el global, no eliminado).

No estaba seguro de si alguien se había topado con una situación similar a esta y había llegado a un resultado. O bien una forma de hacerlo funcionar en mi forma de pensar actual o una nueva versión que proporcione la funcionalidad que estoy buscando sería muy apreciada. Sé que esta publicación ha sido un poco larga. Si se necesita más explicación, me gustaría proporcionarla.

Editar:

He publicado la solución final que utilicé para este problema específico a continuación. Se basa en el enlace al QuerySetManager personalizado de Simon.


Aquí está la solución específica a mi problema usando el QuerySetManager personalizado de Simon al que Scott vinculaba.

from django.db import models from django.contrib import admin from django.db.models.query import QuerySet from django.core.exceptions import FieldError class MixinManager(models.Manager): def get_query_set(self): try: return self.model.MixinQuerySet(self.model).filter(deleted=False) except FieldError: return self.model.MixinQuerySet(self.model) class BaseMixin(models.Model): admin = models.Manager() objects = MixinManager() class MixinQuerySet(QuerySet): def globals(self): try: return self.filter(is_global=True) except FieldError: return self.all() class Meta: abstract = True class DeleteMixin(BaseMixin): deleted = models.BooleanField(default=False) class Meta: abstract = True def delete(self): self.deleted = True self.save() class GlobalMixin(BaseMixin): is_global = models.BooleanField(default=True) class Meta: abstract = True

Cualquier mixin en el futuro que quiera agregar funcionalidad extra al conjunto de consultas simplemente necesita extender BaseMixin (o tenerlo en algún lugar de su jerarquía). Cada vez que intento filtrar la consulta establecida, la envuelvo en un try-catch en caso de que ese campo no exista realmente (es decir, no amplía esa mezcla). El filtro global se invoca usando globals (), mientras que el filtro de eliminación se invoca automáticamente (si se elimina algo, no quiero que se muestre). El uso de este sistema permite los siguientes tipos de comandos:

TemporaryModel.objects.all() # If extending DeleteMixin, no deleted instances are returned TemporaryModel.objects.all().globals() # Filter out the private instances (non-global) TemporaryModel.objects.filter(...) # Ditto about excluding deleteds

Una cosa a tener en cuenta es que el filtro de eliminación no afectará a las interfaces de administración, ya que el Administrador predeterminado se declara primero (convirtiéndolo en el predeterminado). No recuerdo cuándo cambiaron el administrador para usar Model._default_manager en lugar de Model.objects, pero las instancias eliminadas seguirán apareciendo en el administrador (en caso de que necesite eliminarlas).



Pasé un tiempo tratando de encontrar una forma de construir una fábrica agradable para hacer esto, pero estoy teniendo muchos problemas con eso.

Lo mejor que puedo sugerirte es encadenar tu herencia. No es muy genérico, así que no estoy seguro de lo útil que es, pero todo lo que tendrías que hacer es:

class GlobalMixin(DeleteMixin): is_global = models.BooleanField(default=True) objects = GlobalManager() class Meta: abstract = True class GlobalManager(DeleteManager): def globals(self): return self.get_query_set().filter(is_global=1)

Si quieres algo más genérico, lo mejor que se me ocurre es definir un Mixin y un Manager get_query_set() que redefinan get_query_set() (supongo que solo quieres hacer esto una vez; de lo contrario, las cosas se complican bastante) y luego aprobar un lista de campos que desearía agregar a través de Mixin s.

Se vería algo como esto (no probado en absoluto):

class DeleteMixin(models.Model): deleted = models.BooleanField(default=False) class Meta: abstract = True def create_mixin(base_mixin, **kwargs): class wrapper(base_mixin): class Meta: abstract = True for k in kwargs.keys(): setattr(wrapper, k, kwargs[k]) return wrapper class DeleteManager(models.Manager): def get_query_set(self): return super(DeleteManager, self).get_query_set().filter(deleted=False) def create_manager(base_manager, **kwargs): class wrapper(base_manager): pass for k in kwargs.keys(): setattr(wrapper, k, kwargs[k]) return wrapper

Ok, entonces esto es feo, pero ¿qué te tiene? Básicamente, es la misma solución, pero mucho más dinámica y un poco más DRY, aunque más compleja de leer.

Primero, crea su administrador de forma dinámica:

def globals(inst): return inst.get_query_set().filter(is_global=1) GlobalDeleteManager = create_manager(DeleteManager, globals=globals)

Esto crea un nuevo administrador que es una subclase de DeleteManager y tiene un método llamado globals .

Luego, creas tu modelo mixin:

GlobalDeleteMixin = create_mixin(DeleteMixin, is_global=models.BooleanField(default=False), objects = GlobalDeleteManager())

Como dije, es feo. Pero significa que no tiene que redefinir los globals() . Si desea que un tipo diferente de administrador tenga globals() , simplemente llame a create_manager nuevamente con una base diferente. Y puede agregar tantos métodos nuevos como desee. Lo mismo para el administrador, simplemente sigue agregando nuevas funciones que devolverán diferentes conjuntos de consultas.

Entonces, ¿esto es realmente práctico? Tal vez no. Esta respuesta es más un ejercicio en (ab) usando la flexibilidad de Python. No he intentado usar esto, aunque uso algunos de los principios subyacentes de las clases que se extienden dinámicamente para facilitar el acceso.

Avíseme si algo no está claro y actualizaré la respuesta.


Vea este fragmento en Djangosnippets: http://djangosnippets.org/snippets/734/

En lugar de poner sus métodos personalizados en un administrador, usted subclase el conjunto de consultas. Es muy fácil y funciona perfectamente. El único problema que he tenido es con la herencia del modelo, siempre debe definir el administrador en las subclases del modelo (solo: "objects = QuerySetManager ()" en la subclase), aunque hereden el queryset. Esto tendrá más sentido una vez que esté usando QuerySetManager.