2012-09-28 2 views
2

로 표시 ManyToMany 필드, 나는 장고의 사용자 모델로 ManyToMany 관계가 포함 된 모델 베팅 데 :장고 ModelForms : 장고 응용 프로그램에서 단일 선택

class Bet(models.Model): 
    ... 
    participants = models.ManyToManyField(User) 

사용자가 시작할 수 있어야를 양식을 사용하는 새로운 베팅. 지금까지 배팅에는 정확히 두 명의 참가자가 있으며, 그 중 하나는 직접 배팅을 만드는 사용자입니다. 즉, 새로운 베팅 양식에서는 정확히 한 명의 참가자를 선택해야합니다. 내기 데이터를 저장하면 참가자로 베팅 크리에이터가 추가됩니다.

NewBetForm의 ModelForm를 사용하고 있습니다 : 수

class NewBetForm(forms.ModelForm): 
    class Meta: 
     model = Bet 
     widgets = { 
      'participants': forms.Select() 
     } 

    def save(self, user): 
     ... # save user as participant 

공지 참가자 필드에 대한 재정의 위젯은 당신이 하나 명의 참가자를 선택할 수 있습니다.

Enter a list of values. 

나는이 어디에서 오는지 정말 확실하지 않다 :

그러나, 이것은 나에게 유효성 검사 오류를 제공합니다. 개발자 도구에서 POST 데이터를 보면 기본 위젯을 사용하고 하나의 참여자 만 선택하는 것과 똑같은 것으로 보입니다. 그러나 ManyToManyField의 to_python() 메소드가이 데이터에 문제가있는 것처럼 보입니다. 적어도 Select 위젯을 사용 가능하게하면 User 오브젝트가 작성되지 않습니다.

양식에서 참여자 필드를 제외하고이 양식을 직접 정의하면이 문제를 해결할 수 있지만 ModelForm의 용량을 계속 사용할 수 있으면 훨씬 좋을 것입니다. (결국 위젯이 변경된 것입니다.) 어쩌면 내가 어떻게 하는지를 안다면 어떤 방법으로 전달 된 데이터를 조작 할 수 있습니다.

누구든지 문제가 정확히 무엇인지 말할 수 있습니까? 해결할 수있는 좋은 방법이 있습니까?

미리 감사드립니다. 코멘트에 제안으로

편집

:보기의 (관련) 코드.

def new_bet(request): 
    if request.method == 'POST': 
     form = NewBetForm(request.POST) 
     if form.is_valid(): 
      form.save(request.user) 
      ... # success message and redirect 
    else: 
     form = NewBetForm() 
    return render(request, 'bets/new.html', {'form': form}) 

답변

2

장고 코드에 파고 후, 나는 내 자신의 질문에 대답 할 수 있습니다.

문제는 Django의 ModelForm이 모델의 ManyToManyField을 양식의 ModelMultipleChoiceField으로 매핑한다는 것입니다. 이러한 종류의 양식 필드는 위젯 오브젝트가 value_from_datadict() 메소드에서시 v 스를 리턴 할 것으로 기대합니다. 기본 제공된 위젯 ModelMultipleChoiceField (SelectMultiple)은 value_from_datadict()을 대체하여 사용자가 제공 한 데이터에서 목록을 반환합니다. 그러나 Select 위젯을 사용하는 경우 수퍼 클래스의 기본 value_from_datadict() 메서드가 사용됩니다.이 메서드는 단순히 문자열을 반환합니다. ModelMultipleChoiceField은 그런 것을 전혀 좋아하지 않으므로 유효성 검사 오류가 발생합니다. value_from_datadict() 상속 또는 어떤 수준의 장식을 통해 하나 Select의 재정

  1. : 내가 생각할 수있는 솔루션

    .

  2. 새 양식 필드를 만들고 ModelFormsave() 메서드를 조정하여 데이터를 m2m 관계에 저장하여 m2m 필드를 수동으로 처리합니다.

초의 해결책은 덜 장황한 것처럼 보입니다. 그래서 그것은 제가 계속 될 것입니다.

1

문제는 ManyToMany가이 관계에 대한 잘못된 데이터 형식입니다.

어떤 의미에서 내기 은 다 대다 관계입니다. 참가자를 manytomanyfield로 갖는 것은 이치에 맞지 않습니다. 사용자에게 두 개의 ForeignKeys가 있습니다. 하나는 작성자 용이고 다른 하나는 다른 사용자 용입니다 (수락 자)

+0

m2m 관계의 배경은 나중에 두 명 이상의 참가자가있는 베팅에 기능을 확장 할 수 있다는 것입니다. 그래서 저는 m2m이 적절하다고 생각합니다. – j0ker

1

확인 전에 (제출) Form.clean_field_name에서 제출 된 값을 수정할 수 있습니다. 이 메서드를 사용하여 선택 항목의 단일 값을 목록에 래핑 할 수 있습니다. 실제로 단지 값이 같은 선택 외모 proivded 무엇을 추측, 그래서있어

class NewBetForm(forms.ModelForm): 
    class Meta: 
     model = Bet 
     widgets = { 
      'participants': forms.Select() 
     } 

    def save(self, user): 
     ... # save user as participant 

    def clean_participants(self): 
     data = self.cleaned_data['participants'] 
     return [data] 

이 조정이의 조금을해야 할 수도 있습니다,하지만 난 그것을 작동합니다 생각합니다.

Here are the docs.

+0

나는 아직도 그것을 시도했다. 'clean_participants()'는 절대로 호출되지 않기 때문에 작동하지 않습니다. 폼이 파이썬 사용자 객체를 생성하지 못하는 것으로 보이기 때문에 이미 실패합니다. 'clean()'메소드를 오버라이드하려고하면'self.cleaned_data [ 'participants']'가 설정되지 않습니다. – j0ker

+0

'ModelForm.save'는 매개 변수를 취하지 않습니다. 저장 방법을 복사했지만 비표준 방식을 사용하지 않는 한 서명은'save (self)'이어야합니다. – dokkaebi

+0

'save (self, user)'는 오버라이드가 아니라'super (...). save()'를 호출하고 전달 된 사용자를 참여자 (더하기)로 저장하는 새로운 메소드입니다. – j0ker

1

해결 된 질문을 되 살릴 수는 없지만 이런 해결책을 찾고 있었고 다른 사람들을 돕기 위해 내 코드를 공유 할 것이라고 생각했습니다.

j0ker의 답변에서 그는이 작업을 수행하는 두 가지 방법을 나열합니다. 방법 1을 사용했습니다. SelectMultiple 위젯에서 'value_from_datadict'메소드를 빌 렸습니다.

class M2MSelect(forms.SelectMultiple): 
    def render(self, name, value, attrs=None, choices=()): 
     rendered = super(M2MSelect, self).render(name, value=value, attrs=attrs, choices=choices) 
     return rendered.replace(u'multiple="multiple"', u'') 

많은에 많은 첫 번째가 표시되고 저장 될 때 : @Ryan Currah에서 영감을 forms.py

from django.utils.datastructures import MultiValueDict, MergeDict 

class M2MSelect(forms.Select): 
    def value_from_datadict(self, data, files, name): 
     if isinstance(data, (MultiValueDict, MergeDict)): 
      return data.getlist(name) 
     return data.get(name, None)  

class WindowsSubnetForm(forms.ModelForm): 
    port_group = forms.ModelMultipleChoiceField(widget=M2MSelect, required=True, queryset=PortGroup.objects.all()) 
    class Meta: 
     model = Subnet 
0

나는이 상자 밖으로 작동하는 발견 선택한 값만 남습니다.