読者です 読者をやめる 読者になる 読者になる

Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Django e-commerce part63 Crafting the Order Part 3

Order内容の修正

現状では、ゲストユーザーが買い物を続けようとすると、anonymous userは認証できないという画面が出てきてしまう。それを修正する。

errorの原因は、AddressSelectFormViewの、get_form()の箇所だと思われる。

orders/views.py に追記する。

try をなくして、変数関連を少し整える。

class AddressSelectFormView(FormView):
...
    def get_form(self, *args, **kwargs):
        form = super(AddressSelectFormView, self).get_form(*args, **kwargs)

        user_check_id = self.request.session.get("user_checkout_id") #carts/views.py から持ってくる。
        user_checkout = UserCheckout.objects.get(id=user_check_id)
...
  • user_check_idを、sessionからゲットする。
  • ゲットした、user_check_idでフィルターをかけて、usercheckoutのinstanceをゲットする。

orders/views.py にさらに追記する。

class AddressSelectFormView(FormView):
    form_class = AddressForm
    template_name = "orders/address_select.html"

    def get_form(self, *args, **kwargs):
        form = super(AddressSelectFormView, self).get_form(*args, **kwargs)

        user_check_id = self.request.session.get("user_checkout_id")
        user_checkout = UserCheckout.objects.get(id=user_check_id)

        b_address = UserAddress.objects.filter(
                user=user_checkout,
                type='billing',
                )

        s_address = UserAddress.objects.filter(
                user=user_checkout,
                type='shipping',
                )

        form.fields["billing_address"].queryset = b_address
        form.fields["shipping_address"].queryset = s_address

        if b_address.count() == 0 or s_address.count() == 0:
            return print ("error")

        return form

orders/forms.py に UserAddressFormを作成する。

ModelFormで作成する。

class UserAddressForm(forms.ModelForm):
    class Meta:
        model = UserAddress
        fields = [
            'street',
            'city',
            'state',
            'zipcode',
            'type',
        ]

orders/views.py に、UserAddressCreateView を作成する。

CreateViewは、class based viewにあるため、それを利用する。

Generic editing views | Django documentation | Django

from django.views.generic.edit import CreateView, FormView

from .forms import AddressForm, UserAddressForm
...

class UserAddressCreateView(CreateView):
    form_class = UserAddressForm
    template_name = "forms.html"
    success_url = "/checkout/address/"
...

ポイント

  • CreateViewをimportして、form_classには、UserAddressFormを設定している。
  • success_urlも指定する。

urls.pyを設定

    url(r'^checkout/address/add/$', UserAddressCreateView.as_view(), name='user_address_create'),

orders/views.py の修正

        if b_address.count() == 0 or s_address.count() == 0:
            return redirect("user_address_create")
  • アドレス登録のどちらかが、0である場合は、user_address_createにredirectする。ただこのままだと、shippingaddressが必須項目になってしまって、errorが出てしまうので、ちょっと上記の箇所はコメントアウトしておく。

orders/vews.pyの編集

class AddressSelectFormView(FormView):
    form_class = AddressForm
    template_name = "orders/address_select.html"

    def get_addresses(self, *args, **kwargs):
        user_check_id = self.request.session.get("user_checkout_id")
        user_checkout = UserCheckout.objects.get(id=user_check_id)
        b_address = UserAddress.objects.filter(
                user=user_checkout,
                type='billing',
                )
        s_address = UserAddress.objects.filter(
                user=user_checkout,
                type='shipping',
                )

        return b_address, s_address

    def get_form(self, *args, **kwargs):
        form = super(AddressSelectFormView, self).get_form(*args, **kwargs)

        b_address, s_address = self.get_addresses()
        # 
        # if b_address.count() == 0 or s_address.count() == 0:
        #     return redirect("user_address_create")

        form.fields["billing_address"].queryset = b_address
        form.fields["shipping_address"].queryset = s_address

        return form

ポイント

  • b_addressと、s_addressをgetするfunctionを新たに作成する。
  • get_formでは、self.get_addresses()で、それぞれの値を代入する。(これは、2つの値を返すため、変数も2つ必要)

orders/vews.py AddressSelectFormViewのdispatch メソッドを更新する。

    def dispatch(self, *args, **kwargs):
        b_address, s_address = self.get_addresses()
        if b_address.count() == 0 or s_address.count() == 0:
            return redirect("user_address_create")

        return super(AddressSelectFormView, self).dispatch(*args, **kwargs)
  • billing shippingのどちらかが0の場合は、user_address_createにredirectする。

  • さらに上記にmessagesを加えると以下のようになる。

    def dispatch(self, *args, **kwargs):
        b_address, s_address = self.get_addresses()
        if b_address.count() == 0:
            messages.success(self.request,"請求先の住所を登録してください。")
            return redirect("user_address_create")
        elif s_address.count() == 0:
            messages.success(self.request,"送付先の住所を登録してください。")
            return redirect("user_address_create")
        else:
            return super(AddressSelectFormView, self).dispatch(*args, **kwargs)

UserAddressForm に必須のuser fieldの情報を登録する。

UserAddress は、user情報が必須なので、その情報をデータベースに渡す必要がある。現状のフォームでは、userは選択できないため、バックヤードで渡す必要あり。ちなみに、ここのuserは、UserCheckout のuserのため、UserCheckoutのインスタンスが必要。

class UserAddressCreateView(CreateView):
...
     def get_checkout_user(self):
        user_check_id = self.request.session.get("user_checkout_id")
        user_checkout = UserCheckout.objects.get(id=user_check_id)
        return user_checkout

    def form_valid(self, form, *args, **kwargs):
        form.user = self.get_checkout_user()
        return super(UserAddressCreateView, self).form_valid(form, *args, **kwargs)
...

ポイント

  • get_checkout メソッドで、sessionからcheckout_idをゲット->絞り込み->user_checkoutにUserCheckoutのインスタンスを渡す。
  • form_validのタイミングで、form.user (ここでいうformは、UserAddressFormを想定)に、user_checkoutを代入する(ように、form_validを上書きする)

error を修正する。

以下のように、orders/useraddressには、user_idが必須と出てしまうので、それを修正する。つまり、user_checkoutがうまく渡せていないということだと思われる。

NOT NULL constraint failed: orders_useraddress.user_id

form_validを以下のように修正する。

    def form_valid(self, form, *args, **kwargs):
        form.instance.user = self.get_checkout_user()
        return super(UserAddressCreateView, self).form_valid(form, *args, **kwargs)

ポイント

  • UserAddressForm(forms.ModelForm)のように、ModelFormは、form.instance.フィールド名で、formのインスタンスをゲットできる。form.フィールド名では、ゲットできない。これはハマりそうなポイントのため注意。