복붙노트

[PYTHON] Django ModelForm - 다 대다 필드

PYTHON

Django ModelForm - 다 대다 필드

다음 모델 및 양식을 고려하십시오.

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

class Topping(models.Model):
    name = models.CharField(max_length=50)
    ison = models.ManyToManyField(Pizza, blank=True)

class ToppingForm(forms.ModelForm):
    class Meta:
        model = Topping

ToppingForm을 보았을 때 어떤 토핑을 진행할 지 선택할 수 있으며 모든 것이 멋지다.

내 질문은 : 피자와 토핑 사이의 다 대다 관계를 활용할 수있는 ModelForm for Pizza를 정의하고 피자를 토핑하는 것을 선택할 수있게하려면 어떻게해야합니까?

해결법

  1. ==============================

    1.Django가 자동으로 그렇게하지 않으므로 PizzaForm에 새 ModelMultipleChoiceField를 추가하고 수동으로 해당 양식 필드를 모델 필드에 연결하는 것이 여기에있는 것 같습니다.

    Django가 자동으로 그렇게하지 않으므로 PizzaForm에 새 ModelMultipleChoiceField를 추가하고 수동으로 해당 양식 필드를 모델 필드에 연결하는 것이 여기에있는 것 같습니다.

    다음 스 니펫이 도움이 될 수 있습니다.

    class PizzaForm(forms.ModelForm):
        class Meta:
            model = Pizza
    
        # Representing the many to many related field in Pizza
        toppings = forms.ModelMultipleChoiceField(queryset=Topping.objects.all())
    
        # Overriding __init__ here allows us to provide initial
        # data for 'toppings' field
        def __init__(self, *args, **kwargs):
            # Only in case we build the form from an instance
            # (otherwise, 'toppings' list should be empty)
            if kwargs.get('instance'):
                # We get the 'initial' keyword argument or initialize it
                # as a dict if it didn't exist.                
                initial = kwargs.setdefault('initial', {})
                # The widget for a ModelMultipleChoiceField expects
                # a list of primary key for the selected data.
                initial['toppings'] = [t.pk for t in kwargs['instance'].topping_set.all()]
    
            forms.ModelForm.__init__(self, *args, **kwargs)
    
        # Overriding save allows us to process the value of 'toppings' field    
        def save(self, commit=True):
            # Get the unsave Pizza instance
            instance = forms.ModelForm.save(self, False)
    
            # Prepare a 'save_m2m' method for the form,
            old_save_m2m = self.save_m2m
            def save_m2m():
               old_save_m2m()
               # This is where we actually link the pizza with toppings
               instance.topping_set.clear()
               instance.topping_set.add(*self.cleaned_data['toppings'])
            self.save_m2m = save_m2m
    
            # Do we need to save all changes now?
            if commit:
                instance.save()
                self.save_m2m()
    
            return instance
    

    이 PizzaForm은 admin에서도 사용할 수 있습니다.

    # yourapp/admin.py
    from django.contrib.admin import site, ModelAdmin
    from yourapp.models import Pizza
    from yourapp.forms import PizzaForm
    
    class PizzaAdmin(ModelAdmin):
      form = PizzaForm
    
    site.register(Pizza, PizzaAdmin)
    

    save () 메소드는 다소 장황 할 수 있지만, commit = False 상황을 지원할 필요가 없다면 단순화 할 수있다.

    def save(self):
      instance = forms.ModelForm.save(self)
      instance.topping_set.clear()
      instance.topping_set.add(*self.cleaned_data['toppings'])
      return instance
    
  2. ==============================

    2.나는 100 % 질문을 받는다는 것을 확신하지 못한다. 그래서 나는이 가정을 가지고 도망 갈 것이다 :

    나는 100 % 질문을 받는다는 것을 확신하지 못한다. 그래서 나는이 가정을 가지고 도망 갈 것이다 :

    각 피자는 많은 토핑을 가질 수 있습니다. 각 토핑에는 많은 피자가있을 수 있습니다. 그러나 Topping이 Pizza에 추가되면, Topping은 자동적으로 Pizza를 가지며 그 반대도 마찬가지입니다.

    이 경우 가장 좋은 방법은 Django가 아주 잘 지원하는 관계 테이블입니다. 다음과 같이 보일 수 있습니다.

    models.py

    class PizzaTopping(models.Model):
        topping = models.ForeignKey('Topping')
        pizza = models.ForeignKey('Pizza')
    class Pizza(models.Model):     
        name = models.CharField(max_length=50) 
        topped_by = models.ManyToManyField('Topping', through=PizzaTopping)
        def __str__(self):
            return self.name
        def __unicode__(self):
            return self.name
    class Topping(models.Model):   
        name=models.CharField(max_length=50)
        is_on = models.ManyToManyField('Pizza', through=PizzaTopping)
        def __str__(self):
            return self.name
        def __unicode__(self):
            return self.name
    

    forms.py

    class PizzaForm(forms.ModelForm):
        class Meta:
            model = Pizza
    class ToppingForm(forms.ModelForm):
        class Meta:
            model = Topping
    

    예:

    >>> p1 = Pizza(name="Monday")
    >>> p1.save()
    >>> p2 = Pizza(name="Tuesday")
    >>> p2.save()
    >>> t1 = Topping(name="Pepperoni")
    >>> t1.save()
    >>> t2 = Topping(name="Bacon")
    >>> t2.save()
    >>> PizzaTopping(pizza=p1, topping=t1).save() # Monday + Pepperoni
    >>> PizzaTopping(pizza=p2, topping=t1).save() # Tuesday + Pepperoni
    >>> PizzaTopping(pizza=p2, topping=t2).save() # Tuesday + Bacon
    
    >>> tform = ToppingForm(instance=t2) # Bacon
    >>> tform.as_table() # Should be on only Tuesday.
    u'<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="Bacon" maxlength="50" /></td></tr>\n<tr><th><label for="id_is_on">Is on:</label></th><td><select multiple="multiple" name="is_on" id="id_is_on">\n<option value="1">Monday</option>\n<option value="2" selected="selected">Tuesday</option>\n</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>'
    
    >>> pform = PizzaForm(instance=p1) # Monday
    >>> pform.as_table() # Should have only Pepperoni
    u'<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="Monday" maxlength="50" /></td></tr>\n<tr><th><label for="id_topped_by">Topped by:</label></th><td><select multiple="multiple" name="topped_by" id="id_topped_by">\n<option value="1" selected="selected">Pepperoni</option>\n<option value="2">Bacon</option>\n</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>'
    
    >>> pform2 = PizzaForm(instance=p2) # Tuesday
    >>> pform2.as_table() # Both Pepperoni and Bacon
    u'<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="Tuesday" maxlength="50" /></td></tr>\n<tr><th><label for="id_topped_by">Topped by:</label></th><td><select multiple="multiple" name="topped_by" id="id_topped_by">\n<option value="1" selected="selected">Pepperoni</option>\n<option value="2" selected="selected">Bacon</option>\n</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>'
    
  3. ==============================

    3.솔직하게 말해서, 나는 many-to-many 관계를 피자 모델에 넣을 것입니다. 나는 이것을 현실에 가깝게 생각한다. 여러 피자를 주문한 사람을 상상해보십시오. 그는 "피자 1과 2에 치즈를, 피자 1과 3에 토마토를 넣고 싶습니다"라고 말하지는 않을 것입니다. 그러나 "치즈가있는 피자 한 개, 치즈와 토마토가있는 피자 한 개"라고 말할 것입니다.

    솔직하게 말해서, 나는 many-to-many 관계를 피자 모델에 넣을 것입니다. 나는 이것을 현실에 가깝게 생각한다. 여러 피자를 주문한 사람을 상상해보십시오. 그는 "피자 1과 2에 치즈를, 피자 1과 3에 토마토를 넣고 싶습니다"라고 말하지는 않을 것입니다. 그러나 "치즈가있는 피자 한 개, 치즈와 토마토가있는 피자 한 개"라고 말할 것입니다.

    당연히 당신의 방법으로 작동하는 양식을 얻을 수 있지만 함께 갈 것입니다 :

    class Pizza(models.Model):
        name = models.CharField(max_length=50)
        toppings = models.ManyToManyField(Topping)
    
  4. ==============================

    4.이게 당신이 찾고있는 것이지 모르겠지만, 피자에 topping_set 속성이 있다는 것을 알고 있습니까? 이 속성을 사용하면 ModelForm에 새로운 토핑을 쉽게 추가 할 수 있습니다.

    이게 당신이 찾고있는 것이지 모르겠지만, 피자에 topping_set 속성이 있다는 것을 알고 있습니까? 이 속성을 사용하면 ModelForm에 새로운 토핑을 쉽게 추가 할 수 있습니다.

    new_pizza.topping_set.add(new_topping)
    
  5. ==============================

    5.우리는 장고 관리자를 사용하여 우리의 애플 리케이션에서 비슷한 문제가 발생했습니다. 사용자와 그룹 간에는 많은 관계가 있으며 사용자를 그룹에 쉽게 추가 할 수 없습니다. 나는 장고를위한 패치를 만들었지 만 그렇게하지는 않는다. ;-) 당신은 그것을 읽고 피자 / 토 핑 문제와 유사한 솔루션을 적용하려고 시도 할 수있다. 이 방법으로 토핑 안에 있으면 관련 피자를 쉽게 추가 할 수 있습니다.

    우리는 장고 관리자를 사용하여 우리의 애플 리케이션에서 비슷한 문제가 발생했습니다. 사용자와 그룹 간에는 많은 관계가 있으며 사용자를 그룹에 쉽게 추가 할 수 없습니다. 나는 장고를위한 패치를 만들었지 만 그렇게하지는 않는다. ;-) 당신은 그것을 읽고 피자 / 토 핑 문제와 유사한 솔루션을 적용하려고 시도 할 수있다. 이 방법으로 토핑 안에 있으면 관련 피자를 쉽게 추가 할 수 있습니다.

  6. ==============================

    6.이를 달성하기위한 또 다른 간단한 방법은 중개 테이블을 생성하고 인라인 필드를 사용하여이를 완료하는 것입니다. https://docs.djangoproject.com/en/1.2/ref/contrib/admin/#working-with-many-to-many-intermedi- models을 참조하십시오.

    이를 달성하기위한 또 다른 간단한 방법은 중개 테이블을 생성하고 인라인 필드를 사용하여이를 완료하는 것입니다. https://docs.djangoproject.com/en/1.2/ref/contrib/admin/#working-with-many-to-many-intermedi- models을 참조하십시오.

    아래의 일부 샘플 코드

    models.py

    class Pizza(models.Model):
        name = models.CharField(max_length=50)
    
    class Topping(models.Model):
        name = models.CharField(max_length=50)
        ison = models.ManyToManyField(Pizza, through='PizzaTopping')
    
    class PizzaTopping(models.Model):
        pizza = models.ForeignKey(Pizza)
        topping = models.ForeignKey(Topping)
    

    admin.py

    class PizzaToppingInline(admin.TabularInline):
        model = PizzaTopping
    
    class PizzaAdmin(admin.ModelAdmin):
        inlines = [PizzaToppingInline,]
    
    class ToppingAdmin(admin.ModelAdmin):
        inlines = [PizzaToppingInline,]
    
    admin.site.register(Pizza, PizzaAdmin)
    admin.site.register(Topping, ToppingAdmin)
    
  7. ==============================

    7.나는 사용자 관리 양식을 사용하여 Clément 코드를 기반으로 유사한 작업을 수행했습니다.

    나는 사용자 관리 양식을 사용하여 Clément 코드를 기반으로 유사한 작업을 수행했습니다.

    # models.py
    class Clinica(models.Model):
      ...
      users = models.ManyToManyField(User, null=True, blank=True, related_name='clinicas')
    
    # admin.py
    class CustomUserChangeForm(UserChangeForm):
      clinicas = forms.ModelMultipleChoiceField(queryset=Clinica.objects.all())
    
      def __init__(self,*args,**kwargs):
        if 'instance' in kwargs:
          initial = kwargs.setdefault('initial',{})
          initial['clinicas'] = kwargs['instance'].clinicas.values_list('pk',flat=True)
        super(CustomUserChangeForm,self).__init__(*args,**kwargs)
    
      def save(self,*args,**kwargs):
        instance = super(CustomUserChangeForm,self).save(*args,**kwargs)
        instance.clinicas = self.cleaned_data['clinicas']
        return instance
    
      class Meta:
        model = User
    
    admin.site.unregister(User)
    
    UserAdmin.fieldsets += ( (u'Clinicas', {'fields': ('clinicas',)}), )
    UserAdmin.form = CustomUserChangeForm
    
    admin.site.register(User,UserAdmin)
    
  8. from https://stackoverflow.com/questions/2216974/django-modelform-for-many-to-many-fields by cc-by-sa and MIT license