2010-10-08 17 views
13

Jak ustawić entry.category jako instancję CategoryProxy? Zobacz kod do szczegółów:Model proxy Django i 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 

Odlewanie z kategorii do CategoryProxy jest ok też, ale nie jestem bardzo obeznany z wewnętrznymi ORM prawidłowo kopiują stan wewnętrzny ...

edytować. Powód: dodałem metodę CategoryProxy i chcesz używać go:

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

EDIT 2. Obecnie realizowane to tak:

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

ale wygląda straszne ...

+0

Jakiś konkretny powód, dla którego chcesz to zrobić? –

+0

Dodałem metodę do CategoryProxy i chcę go użyć, tak jak EntryProxy.objects.get (pk = 1) .category.method_at_category_proxy() –

Odpowiedz

11

Aby przejść z klasy do klasy modelu proxy bez uderzania bazy danych:

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

EDIT: fragment powyżej nie wydaje się pracy nad Django 1.4.

Ponieważ Django 1.4, biorę wszystkie pola wartości ręcznie tak:

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 

Aby przełączyć się z queryset do klasy proxy dziecko bez bicia bazy danych:

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

+1 +1 dziękuję! Jak wymyśliłeś to? Byłem ciekawy, czy miałeś wpis na blogu lub coś, co wyjaśniało szczegółowo, w jaki sposób działa QuerySet.model ... –

+0

Po raz pierwszy znalazłem go tutaj, w stackoverflow, na sekundę, znalazłem go sam w teście powłoki bpython. Zadowolony, że to pomogło. – christophe31

-1

Definiowanie właściwości category w EntryProxy, która wyszukuje CategoryProxy za pomocą jej id:

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

    class Meta: 
     proxy = True 
+1

Oznacza to trafienie bazy danych dla każdego wyszukiwania kategorii! A co, jeśli mamy pętlę? –

0

Joseph Spiros do jednego z moich pytań może pomóc:

Django Inheritance and Permalinks

Nie jestem pewien, jak to będzie działać z modelami proxy.

+0

Prawdopodobnie można osiągnąć coś porównywalnego, dodając pole dyskryminatora lub typ zawartości do modelu podstawowego i hakując dostosowanego menedżera modelu. Jednak nie jestem pewien, czy odzwierciedla to, co zamierzał Vladimir. Może potrzebować różnych poglądów na swoje dane, dlatego wybrał modele proxy. Mogę sobie wyobrazić, że chce on nawet móc definiować różne kategorie modeli proxy i oczekuje, że odniesienia między tymi modelami zostaną rozwiązane do serwerów proxy tej samej kategorii. Nie byłoby to możliwe, gdyby typ proxy był przechowywany w modelu podstawowym. –

-1

Adaptacja odpowiedź Bernda Petersohn nieznacznie , mamy wtedy:

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

To powinno być bardziej ekonomiczne z Baza danych. Aby uzyskać dodatkowe ulepszenia, możesz ustawić atrybut prywatny (self._category) przy pierwszym wywołaniu metody, a następnie zwrócić to wszystko później.

+1

Dochodzi do obniżki, ponieważ ma ona trafienie w bazie danych. – knite

2

To pytanie ma już akceptowaną odpowiedź, ale chciałem opublikować to dla każdego, kto może przyjść szukać.

Można załączyć model w środowisku wykonawczym z nowym polem, aby relacje działały zgodnie z oczekiwaniami.Pełny przykład można zobaczyć tutaj - 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)) 
Powiązane problemy