2017-01-11 3 views
1

우리는 여러 가지 관계와 여러 모델을 가지고 있습니다. 객체의 모든 관련 객체를 얻는 일반적인 방법을 얻으려고합니다. 내 모델 Pessoa에서 ._meta.get_fields()를 인쇄 할 경우Django는 'through'관계 테이블을 사용하여 모델로부터 모든 관련 객체를 가져옵니다.

, 난이 관계 필드 (I는 생략 '정상적인'사람) 수 :

<ManyToManyRel: cadastroimoveis.pessoa> 
<ManyToOneRel: cadastroimoveis.pessoa_pessoa> 
<ManyToOneRel: cadastroimoveis.pessoa_pessoa> 
<ManyToOneRel: cadastroimoveis.pessoa_itr> 
<ManyToManyRel: cadastroimoveis.doc> 
<ManyToOneRel: cadastroimoveis.doc_pessoa> 
cadastroimoveis.Pessoa.relacoes 
cadastroimoveis.Pessoa.itrs 

이 특정 모델에만있다 M2M 관계를, 그리고 모든 그 중 'Here'과 같이 '통과'모델이 포함되어 있습니다.

볼 수 있듯이 모델과 중간 단계 테이블을 통해 하나씩 반복됩니다 (역시 추측할만한 모델 임). 그리고 재귀 관계의 경우 두 번 반복됩니다.

제 질문은 이러한 반복을 피할 수있는 방법이 있습니까?

두 테이블을 스팸하더라도 최종적으로 동일한 관계에 '반복되는 필드'를 알 수있는 방법은 무엇입니까? through 테이블에 필드가 있으면 다른 방식으로 필드를 표시하려고합니다.

그리고 Model _meta API 설명서에 따라, 모든 관련 개체를 얻기 위해 이것을 사용합니다 :

[ 
    f for f in MyModel._meta.get_fields() 
    if (f.one_to_many or f.one_to_one) 
    and f.auto_created and not f.concrete 
] 

그러나 및 테이블이 auto_created 간주되지 않습니다 '을 통해이'콘크리트입니다.

예 :

<ManyToManyRel: cadastroimoveis.ccir> 
<ManyToOneRel: cadastroimoveis.ccir_pessoa> 

이 두 필드 '점'같은 관계 하나가 중간 테이블이고 다른 모델이며,이 두 가지가 있다는 것을 알고하는 (자동) 방법이 상관 되니? 그들이 공유하는 어떤 속성도 찾을 수 없었습니다. 쓰루 테이블 필드가있을 때, 나는 모델 자체에 M2M 분야의 대신에 편집해야하기 때문에

그 이유는

Models.py : http://pastebin.com/szDfhHQ3은 내가

를 할 수있는 최선 청소
+0

여기에 모델을 추가 할 수 있습니까? – AKS

+0

어떤 Django 버전을 사용하고 있습니까? – Risadinha

+0

@AKS 지연에 대한 미안, 내 모델을 추가, 곧 답변을 테스트 할 것입니다 – Mojimi

답변

1

다른 답변은 분명히 내 그림을 이해하는데 도움이되었습니다. 특히 제 모든 관계가 M2M이고 through 테이블을 가지고 있으며, 모든 것이 AJAX/Javascript로 이루어 지므로 응답이 JSON-y입니다. 당신이 관계를 만들기 위해 그 (것)들에있는 객체를 생성해야하기 때문에

는 지금 만 모든 M2M 모델의 테이블을 통해 얻을 수 있지만, 쉽게 다른 모든 관계

def get_relationships(model): 
    fields = list(model._meta.get_fields()) 

    m2m_fields = {} 
    #Getting m2m relationships first 
    for i, field in enumerate(fields): 
     print(field) 
     if field.is_relation: 
      if field.many_to_many: 
       fields.pop(i) 
       try: 
        #If its a forward field, we want the relationship instead 
        if not hasattr(field,'field'): 
         field = field.remote_field 
       except AttributeError: 
        pass 
       if hasattr(field,'through'): 
        through = field.through 
        #In case of recursive relationships, there will be duplicates so we don't need to do it again 
        if m2m_fields.get(through._meta.model.__name__): 
         continue 
        m2m_fields[through._meta.model.__name__] = {} 
        m2m = m2m_fields[through._meta.model.__name__] 
        #Finding the models which participate in the through table and the direction 
        m2m['owner'] = {'model' : field.model.__name__} 
        m2m['related'] = {'model' : field.related_model.__name__} 
        recursive = False 
        #Checking recursivity, will use this later 
        #Finding field names for the foreignkeys of the through table 
        for through_field in through._meta.get_fields(): 
         if not (through_field.related_model is None): 
          if m2m['owner']['model'] == through_field.related_model.__name__ and not m2m['owner'].get('field'): 
           m2m['owner']['field'] = through_field.name 
          elif m2m['related']['model'] == through_field.related_model.__name__ and not m2m['related'].get('field'): 
           m2m['related']['field'] = through_field.name 
         elif not through_field.primary_key: 
          if not m2m.get('rel_fields'): 
           m2m['rel_fields'] = [] 
          m2m['rel_fields'].append(through_field.name) 
    #Now removing the through tables from the fields list, because they appear as a regular ManyToOne relationship otherwise 
    for through_table in m2m_fields.keys(): 
     name = through_table 
     for i, field in enumerate(fields): 
      if field.many_to_one: 
       if field.__name__ and field.related_model: 
        if field.related_model.__name__ == name: 
         fields.pop(i) 
    #Todo : OneToOne and ManyToOne relationships 

    return m2m_fields 


for key,value in get_relationships(Pessoa).items(): 
    print(key, " = ", value) 

그것은이에게 점점로 확장 될 수 있습니다 하나의 추악한 코드가 있지만 파이썬을 잘 배우지 못하고 일을 배우려고 노력하지만 내 질문에 대한 매력처럼 작동한다고 보증합니다

1

Django 1.10의 경우 다음 코드는 BaseModelForm 코드 (Django 원본)에서 영감을 받았습니다.

다음과 같은 관계가있는 경우 :

class Group(Model): 
    field = .... 

class Person(Model): 
    groups = ManyToManyField(Group, through='Membership') 

class Membership(Model): 
    person = ForeignKey(Person) 
    group = ForeignKey(Group) 
    position = TextField(...) 

는 그 다음 관련 분야 및 특성은 다음과 같이 조회 할 수 있습니다 : 예를 들어

opts = Person._meta 
for f in chain(opts.many_to_many, opts.virtual_fields): 
    if f.rel.through: 
     # this would return "group" 
     attr_field = f.m2m_reverse_field_name() 

     # this is the Membership class (a class instance) 
     m2m_model = f.rel.through 

     # this would return "person" 
     join_field = field.m2m_field_name() 

     # to get all "Membership" objects for "person" personXY 
     qs_filter = {join_field: personXY} 
     qs = m2m_model.objects.filter(**qs_filter) 

     # get the PKs of all groups where personXY is a member of 
     lookup_by_pk = '{}__pk'.format(attr_field) 
     current_pks = qs.values_list(lookup_by_pk, flat=True) if qs.exists() else [] 
+0

이 예제는 역 의존 관계를 얻지 못했습니다 – Mojimi

+0

인용 : "왜냐하면 통과 테이블에 필드가있을 때 편집해야하기 때문입니다 모델 자체의 M2M 필드 대신 "- Person 인스턴스가 있고'Membership' 모델에 저장된 추가 데이터를 저장하려는 경우 해당 코드로이 작업을 수행 할 수 있습니다. – Risadinha

1

을 우리가 모델이 세트가 있습니다. 나는 this django example에서 그것을 집어 들었다.

class Person(models.Model): 
    name = models.CharField(max_length=50) 

class Group(models.Model): 
    name = models.CharField(max_length=128) 
    members = models.ManyToManyField(
     Person, 
     through='Membership', 
     through_fields=('group', 'person'), 
    ) 

class Membership(models.Model): 
    group = models.ForeignKey(Group, on_delete=models.CASCADE) 
    person = models.ForeignKey(Person, on_delete=models.CASCADE) 
    inviter = models.ForeignKey(
     Person, 
     on_delete=models.CASCADE, 
     related_name="membership_invites", 
    ) 
    invite_reason = models.CharField(max_length=64) 

솔루션이 약간 엉망이지만 필요에 따라 최적화 할 수 있습니다.

def get_through_field(f):                        
    opts = f.through._meta                        
    if f.through_fields: 
     return opts.get_field(f.through_fields[1])                  
    for field in opts.fields:                       
     rel = getattr(field, 'remote_field', None)                  
     if rel and rel.model == f.model:                    
      return field 

model = models.Person 

rels = dict(
    (f.field, f) for f in model._meta.get_fields() 
    if f.is_relation 
) 

excludes = set() 
for f in model._meta.get_fields(): 
    if f.many_to_many: 
     through = get_through_field(f) 
     excludes.add(rels[through]) 

for f in model._meta.get_fields(): 
    if f not in excludes: 
     print f.name, f 

출력는 :

group <ManyToManyRel: m.group> 
membership_invites <ManyToOneRel: m.membership> 
id m.Person.id 
name m.Person.name 

당신이 볼 수 있듯이, 더 membership 필드 없다.

+0

이 버전은 테스트를 통과 했습니까? 1.10에서 시도해 보았고'AttributeError : ManyToManyField '객체에 13 행의'field'' 속성이 없습니다. – Mojimi

+0

Django에서 == 1.10.5가 정상적으로 작동합니다. 코드에 버그가 수정되었지만 문제와 관련이 없습니다. 'ManyToManyField'는 이상하게 보입니다. 왜냐하면 get_fields()는 Fields가 아니라 Rels를 반환하기 때문입니다. 귀하의 예에서도 : ', '. 참조? 필드가 없어야합니다. 그걸 다시 확인해주세요. – vasi1y

+0

내 모델에도 필드로 제공되는 '전달'관계가 있기 때문에 필터를 걸었습니다. – Mojimi