2010-10-08 52 views
13

¿Cómo hacer que entry.category sea instancia de CategoryProxy? Ver código para la descarga:Modelo proxy Django y ForeignKey

class Category(models.Model): pass 

class Entry(models.Model): 
    category = models.ForeignKey(Category) 

class EntryProxy(Entry): 
    class Meta: 
     proxy = True 

class CategoryProxy(Category): 
    class Meta: 
     proxy = True 

entry = EntryProxy.objects.get(pk=1) 
entry.category # !!! I want CategoryProxy instance here 

moldeada de la categoría a CategoryProxy también está bien, pero no estoy muy familiarizado con las partes internas de ORM para copiar correctamente el estado interno ...

EDITAR. Motivo: he añadido método para CategoryProxy y quieren usarlo:

EntryProxy.objects.get(pk=1).category.method_at_category_proxy() 

EDITAR 2. Actualmente he implementado de esta manera:

EntryProxy._meta.get_field_by_name('category')[0].rel.to = CategoryProxy 

pero se ve terrible ...

+0

¿Alguna razón en particular por la que desea hacer esto? –

+0

Agregué el método a CategoryProxy y quiero usarlo como EntryProxy.objects.get (pk = 1) .category.method_at_category_proxy() –

Respuesta

11

Para cambiar de una clase de modelo a una clase de proxy sin chocar con la base de datos:

class EntryProxy(Entry): 
    @property 
    def category(self): 
     new_inst = EntryProxy() 
     new_inst.__dict__ = super(EntryProxy, self).category.__dict__ 
     return new_inst 

edición: el fragmento anterior no parece estar trabajando en Django 1.4.

Desde Django 1.4, Tomo todos los campos de valor de forma manual como esto:

class EntryProxy(Entry): 
    @property 
    def category(self): 
     category = super(EntryProxy, self).category 
     new_inst = EntryProxy() 
     for attr in [f.attname for f in category.__class__._meta.fields] + ['_state']: 
      setattr(new_inst, attr, getattr(category, attr)) 
     return new_inst 

cambiar de un conjunto de consultas a una clase de proxy niño sin chocar con la base de datos:

class CategoryProxy(Category): 
    @property 
    def entry_set(self): 
     qs = super(CategoryProxy, self).entry_set 
     qs.model = EntryProxy 
     return qs 
+0

+1 +1 ¡gracias! ¿Cómo se te ocurrió esto? Tenía curiosidad si tuvieras una publicación en el blog o algo que explicara detalladamente cómo funciona QuerySet.model ... –

+0

Por primera vez lo encontré aquí, en stackoverflow, por el segundo, lo encontré en una prueba de shell bpython. Me complace ver que ayudó. – christophe31

-1

Defina una propiedad category en EntryProxy que busca CategoryProxy por su id:

La respuesta de
class EntryProxy(Entry): 
    @property 
    def category(self): 
     cid = super(EntryProxy, self).category.id 
     return CategoryProxy.objects.get(id=cid) 

    class Meta: 
     proxy = True 
+1

Esto implica un hit de base de datos para cada búsqueda de categoría. ¿Qué pasa si estamos bucleando? –

0

José Spiros a una de mis preguntas pueden ayudar:

Django Inheritance and Permalinks

No estoy seguro de cómo se va a trabajar con los modelos de proxy.

+0

Uno probablemente podría lograr algo comparable al agregar un discriminador o campo de tipo de contenido al modelo base y piratear un administrador de modelo personalizado. Sin embargo, no estoy seguro de que esto refleje lo que Vladimir pretendía. Es posible que solo necesite diferentes puntos de vista sobre sus datos, por lo tanto, eligió modelos proxy. Me imagino que incluso quiere poder definir diferentes categorías de modelos de proxy y espera que las referencias entre estos modelos se resuelvan con proxies de la misma categoría. Esto no sería posible si el tipo del proxy está almacenado en el modelo base. –

-1

Adaptación de la respuesta de Bernd Petersohn ligeramente , entonces tenemos:

class EntryProxy(Entry): 
    @property 
    def category(self): 
     return CategoryProxy.objects.get(id=self.category_id) 

Esto debería ser más económico con el base de datos Para mejoras adicionales, puede establecer un atributo privado (self._category) la primera vez que se llame al método, luego devolverlo todas las veces posteriores.

+1

Se ha desestimado porque tiene un golpe de base de datos. – knite

2

Esta cuestión ya tiene una respuesta aceptada, pero Quería publicar esto para cualquier persona que pueda venir a buscar.

Puede parchar el modelo en tiempo de ejecución con el nuevo campo para que las relaciones funcionen como se espera.Aquí se puede ver un ejemplo completo: https://gist.github.com/carymrobbins/8721082

from django.db.models.fields.related import ReverseSingleRelatedObjectDescriptor 

def override_model_field(model, field, field_name, column_name): 
    """Force override a field in a Django Model. 
    Usage: override_model_field(
     MyModel, models.ForeignKey(OtherModel), 'other', 'other_id') 
    :type model: django.db.models.base.ModelBase 
    :type field: django.db.models.fields.Field 
    :type field_name: basestring 
    :type column_name: basestring 
    """ 
    field.name = field_name 
    field.attname = column_name 
    for i, f in enumerate(model._meta.fields): 
     if f.name == field_name: 
      model._meta.fields[i] = field 
      break 
    else: 
     raise TypeError('Model {!r} does not have a field {!r}.' 
         .format(model, field_name)) 
    model.add_to_class(field_name, 
         ReverseSingleRelatedObjectDescriptor(field)) 
Cuestiones relacionadas