0

私のアプリケーションでは、ユーザーグループを使用してユーザーのタイプを表しています。私の特定のケースでは、ユーザーは1つのグループにしか参加できません。実装では、2つのオプションがあります。

  1. ManyToManyをForeignKeyになるようにオーバーライドします
  2. 私のフォームではManyToManyをMultipleChoiceFieldとして表現し、1つの送信のみを受け入れて、そこから移動します。

オプション2を選択したのは、ユーザーを2つのグループに参加させることがテストに役立つ場合があるためです(便宜上)。実装に関しては、この2つに違いはないと思います(ただし、アドバイスをいただければ幸いです)。

私の見解では、2つ(UserのUserProfile拡張クラスのManyToMany)を関連付けるコードを記述します。これが機能しているかどうかはわかりません。

私が遭遇する主なエラーは、フォームが検証を許可せず、ManyToManyが続行するには「値のリスト」が必要であると言っていることです。

私は次のコードセットを持っています:

forms.py

from django.forms import ModelForm, Textarea
from django.contrib.auth.models import User, Group
from registration.models import UserProfile
from django import forms
from django.db import models

class RegistrationForm(ModelForm):
    class Meta:
        model = User
        fields = ('username', 'password', 'email', 'first_name', 'last_name', 'groups')
        widgets = {
            'groups': forms.Select,
            'password': forms.PasswordInput,
        #    'text': Textarea(attrs = {'rows': 3, 'class': 'span10', 'placeholder': 'Post Content'}),
        }

    def __init__(self, *args, **kwargs):
        super(RegistrationForm, self).__init__(*args, **kwargs)
        self.fields['groups'].label = 'Which category do you fall under?'

views.py

def get_registration(request):
    if request.method == 'POST':
        register_form = RegistrationForm(request.POST)
        company_form = CompanyRegistrationForm(request.POST, request.FILES)

        if register_form.is_valid() and company_form.is_valid(): # check CSRF
            if (request.POST['terms'] == True):
                new_user = register_form.save()
                new_company = company_form.save()

                new_profile = UserProfile(user = user, agreed_terms = True)
                new_profile.companies_assoc.add(new_company)
                new_profile.save()

                return HttpResponseRedirect(reverse('companyengine.views.get_company'))
        return render(request, 'registration/register.html', { 'register_form': register_form, 'company_form': company_form } )

    else:
        first_form = RegistrationForm
        second_form = CompanyRegistrationForm
        return render(request, 'registration/register.html', { 'register_form': register_form, 'company_form': company_form } )

およびtemplates.html

<h2>Sign Up</h2>
<form action="/register" method="POST" enctype="multipart/form-data">{% csrf_token %}
    <p>{{ register_form.non_field_error }}</p>
    {% for field in register_form %}
    <div class="control-group">
        {{ field.errors }}
        <label class="control-label">{{ field.label }}</label>
        <div class="controls">
            {{ field }}
        </div>
    </div>
    {% endfor %}

    <div id="company_fields">
        <p>{{ register_form.non_field_error }}</p>
        {% for field in company_form %}
        <div class="control-group">
            {{ field.errors }}
            <label class="control-label">{{ field.label }}</label>
            <div class="controls">
                {{ field }}
            </div>
        </div>
        {% endfor %}
    </div>

    <label><input type="checkbox" name="terms" id="terms"> I agree with the <a href="#">Terms and Conditions</a>.</label>
    <input type="submit" value="Sign up" class="btn btn-primary center">
    <div class="clearfix"></div>
</form>

すべてが完全に正常にロードされているようです。ただし、Groupsフィールドには「値のリスト」が必要なため、フォームはis_valid()を通過しません。TextField / TextAreaからの情報を解析する方法を他の人が尋ねるのを見たことがありますが、情報が1つしかないため、なぜ情報を分割する必要があるのか​​わかりません。

あなたのアドバイスは大歓迎です。

4

1 に答える 1

2

優先ソリューション

まず、M:M の関係を 1:M の関係として使用することを再考する必要があると思います。ユーザーが複数のグループを取得する特定の状況が発生する可能性が高くなり、後の段階でコードにバグが発生して追跡が困難になる可能性があります。

既に UserProfile クラスを使用しているため、ユーザー プロファイル モデルに外部キーを配置すると、存在する必要があるデータ構造の正確な表現が提供されます (テスト時にログインおよびログアウトすることを意味する場合でも)。

アップデート

モデルを次のように変更することで、これを行うことができます。

class UserProfile(models.Model):
    # existing fields here
    single_group = models.ForeignKey(Group)

既存のユーザー グループの関係を使用する既存のコードが多数ある場合、これはあまり実用的なソリューションではありません。ただし、この制限 (ユーザー/ユーザー プロファイルごとに 1 つのグループ) を強制する必要がある場合は、そうします。

特定の問題の解決策

なんらかの理由で、上記の私のコメントが適切でないと感じた場合 (コードが存在する特定の状況はわかりません)...

あなたが経験している問題は、SelectMultiple が値のリストを返すのに対し、選択ウィジェットが単一の項目をフォームに返すという事実によるものだと思います。フォームはリストを想定しているため、ここに問題があります。

SelectMultiple ウィジェットをサブクラス化することをお勧めします。これにより、実際にはフォーム上で単一の選択としてレンダリングされますが、それでも既存のロジックを使用してリストが返されます。

これは、SelectMultiple ウィジェットの現在のレンダリング関数です。

class SelectMultiple(Select):
    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = []
        final_attrs = self.build_attrs(attrs, name=name)
        output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
        options = self.render_options(choices, value)
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe(u'\n'.join(output))

次のように render メソッドをサブクラス化し、オーバーライドした場合:

class CustomSelectSingleAsList(SelectMultiple):
    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = []
        final_attrs = self.build_attrs(attrs, name=name)
        output = [u'<select %s>' % flatatt(final_attrs)] # NOTE removed the multiple attribute
        options = self.render_options(choices, value)
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe(u'\n'.join(output))

これは単一の選択をレンダリングしますが、アイテムのリストを取得します。

次に、フォーム メタで、新しいカスタム クラスを使用します。

widgets = { 'groups': myforms.CustomSelectSingleAsList, 'password': forms.PasswordInput, # 'text': Textarea(attrs = {'rows': 3, 'class': 'span10', 'placeholder': 'Post Content '}), }

または、Select ウィジェットをオーバーライドしてリストを返すこともできます。

class SelectSingleAsList(Select):
    def value_from_datadict(self, data, files, name):
        if isinstance(data, (MultiValueDict, MergeDict)):
            return data.getlist(name)  # NOTE this returns a list rather than a single value.
        return data.get(name, None)

これらのいずれかが問題を解決するかどうか教えてください。

于 2013-02-20T02:07:00.243 に答える