Overriding the AbstractAddress model - some issues

278 views
Skip to first unread message

shafiqu...@gmail.com

unread,
Jun 22, 2014, 4:46:07 PM6/22/14
to django...@googlegroups.com
Hello all, I think this post might be of interest to those interested in the following posts:

"I have overridden the order.ShippingAddressForm but dashboard.orders.views.ShippingAddressUpdateView isnt picking it up"


Objective: I wanted to change the fields that are presented at checkout and for the address book. My original approach was to for the address app to override the AbstractAddress model to delete the title, postcode and line3 fields. I wanted to do this because I just didn't want these fields presented to the user, and I had no use for them (this is for a site in a country where postal codes are not useful). This, however, caused problems because of the way the get_classes method loading.py file loads oscar and local modules. The loader will always import the PartnerAddressForm class from the oscar module:

oscar/apps/dashboard/partners/forms.py

even if you fork dahsborad.partners and override the form - that is, even if you don't use the oscar module. The relevant section in loading.py is:

    # e.g. split 'dashboard.catalogue.forms' in 'dashboard.catalogue', 'forms'
    package, module = module_label.rsplit('.', 1)

    # import from Oscar package (should succeed in most cases)
    # e.g. 'oscar.apps.dashboard.catalogue.forms'
    oscar_module_label = "oscar.apps.%s" % module_label
    oscar_module = _import_module(oscar_module_label, classnames)

At the bottom of the forms.py file in the oscar module is the following class definition, which will always get imported even if you fork and try to override it:

class PartnerAddressForm(forms.ModelForm):

    class Meta:
        fields = ('line1', 'line2', 'line3', 'line4',
                  'state', 'postcode', 'country')
        model = PartnerAddress

So if you delete any of the fields in the tuple above from the AbstractAddress model that you override, you will get an error when you try to use any sublcass of AbstractAddress as a model for a model form. The above class PartnerAddressForm will always get imported, and if any of the fields in the fields variable are missing, an error is raised. 

So my workaround for this was to do the following:

1. Fork the address module, and keep the fields but override the ensure_postcode_is_valid_for_country method so that it does nothing. In the forms.py of that module I also got rid of PhoneNumberMixin (I want flexibility to enter phone numbers in various formats, with/without the country code, etc.). 
2. To hide the title, line3 and postcode fields I modified TWO files:
(a) In my forked address module, in the forms.py file I added these three fields to the fields variable in the Meta class of the UserAddressForm class:

class UserAddressForm(AbstractAddressForm):

    class Meta:
        model = UserAddress
        exclude = ('user', 'num_orders', 'hash', 'search_text',
                   'is_default_for_billing', 'is_default_for_shipping',
                   'title', 'line3', 'postcode',)

    def __init__(self, user, *args, **kwargs):
        super(UserAddressForm, self).__init__(*args, **kwargs)
        self.instance.user = user

(b) I overrode the checkout module, and in that forms.py file I added these three fields to the Meta class of the ShippingAddressForm class:

class ShippingAddressForm(AbstractAddressForm):

    def __init__(self, *args, **kwargs):
        super(ShippingAddressForm, self).__init__(*args, **kwargs)
        self.adjust_country_field()

    def adjust_country_field(self):
        countries = Country._default_manager.filter(
            is_shipping_country=True)

        # No need to show country dropdown if there is only one option
        if len(countries) == 1:
            del self.fields['country']
            self.instance.country = countries[0]
        else:
            self.fields['country'].queryset = countries
            self.fields['country'].empty_label = None

    class Meta:
        model = get_model('order', 'shippingaddress')
        exclude = ('user', 'search_text', 'title', 'postcode', 'line3')

This seems to have done the trick. Took me a long time to figure out- I hope it saves others some time.

Cheers,

Maik Hoepfel

unread,
Jun 26, 2014, 11:18:37 AM6/26/14
to django...@googlegroups.com
Hi Shafique,

> Objective: I wanted to change the fields that are presented at checkout
> and for the address book. My original approach was to for the address
> app to override the AbstractAddress model to delete the title, postcode
> and line3 fields.

Generally, it's hard and not advisable to delete fields from models.
Oscar will expect those fields to be present. If you want to hide them
from users, you should generally hide them in the appropriate forms.
I'm not sure I understand. But I just checked the
dashboard/partners/forms.py, and PartnerAddress gets loaded via
get_model(). So while we don't recommend you completely switch out the
model, you should be able to in this instance. It should use your
PartnerAddress model (which you need to define, obviously).

> > So if you delete any of the fields in the tuple above from the
> AbstractAddressmodel that you override, you will get an error when you
> try to use any sublcass of AbstractAddress as a model for a model form.
> The above class PartnerAddressForm will always get imported, and if any
> of the fields in the fieldsvariable are missing, an error is raised.
>
> So my workaround for this was to do the following:
>
> 1. Fork the addressmodule, and keep the fields but override the
> ensure_postcode_is_valid_for_country method so that it does nothing.

That's sensible. But what country do you live in? The validation
shouldn't do anything for countries without countries.

> In
> the forms.py of that module I also got rid of PhoneNumberMixin (I want
> flexibility to enter phone numbers in various formats, with/without the
> country code, etc.).

The phone number mixin should allow entering phone numbers without a
country code. It then uses the set country to validate.
> --
> https://github.com/tangentlabs/django-oscar
> http://django-oscar.readthedocs.org/en/latest/
> https://twitter.com/django_oscar
> ---
> You received this message because you are subscribed to the Google
> Groups "django-oscar" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-oscar...@googlegroups.com
> <mailto:django-oscar...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-oscar.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-oscar/81493696-7fa4-462d-9cba-ca0ee35b94ba%40googlegroups.com
> <https://groups.google.com/d/msgid/django-oscar/81493696-7fa4-462d-9cba-ca0ee35b94ba%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

shafiqu...@gmail.com

unread,
Jun 26, 2014, 11:49:52 AM6/26/14
to django...@googlegroups.com
Dear Maik,

Thanks for replying to this. The country in question is Tajikistan - a postal code there covers an area so broad that the postal code is not very useful. I didn't want people running into problems with validation if they entered a partial or incorrect code, because it seems the validation runs even when the field is optional. So I did like you suggested and hid the fields in the forms. I didn't know how robust the PhoneNumberMixin is so I just removed the mixin, which should work fine for me (I can contact customers via email if they enter their phone numbers incorrectly).

Thanks!

Maik Hoepfel

unread,
Jun 27, 2014, 10:16:10 AM6/27/14
to django...@googlegroups.com
Hi Shafique,

oh nice! I wanted to ride my motorbike through the 'stans :)

The PhoneNumberMixin is, a few layers down, using a library by Google
which they use for the Android caller. I'd assume it's pretty robust!

I just had a look at the postcodes validation. We're basing it on a list
from Wikipedia:
https://github.com/tangentlabs/django-oscar/blob/master/oscar/apps/address/abstract_models.py#L29

And it's happening here:
https://github.com/tangentlabs/django-oscar/blob/master/oscar/apps/address/abstract_models.py#L260

So yes, I think for Oscar it's a sensible default. If you want to
disable the validation, you have to prevent
ensure_postcode_is_valid_for_country being called, which is called in
the clean() method. So just override that.

I hope that helps.

Cheers,

Maik
> <https://github.com/tangentlabs/django-oscar>
> > http://django-oscar.readthedocs.org/en/latest/
> <http://django-oscar.readthedocs.org/en/latest/>
> > https://twitter.com/django_oscar <https://twitter.com/django_oscar>
> > ---
> > You received this message because you are subscribed to the Google
> > Groups "django-oscar" group.
> > To unsubscribe from this group and stop receiving emails from it,
> send
> > an email to django-oscar...@googlegroups.com <javascript:>
> > <mailto:django-oscar...@googlegroups.com <javascript:>>.
> <http://groups.google.com/group/django-oscar>.
> <https://groups.google.com/d/msgid/django-oscar/81493696-7fa4-462d-9cba-ca0ee35b94ba%40googlegroups.com?utm_medium=email&utm_source=footer
> <https://groups.google.com/d/optout>.
>
> --
> https://github.com/tangentlabs/django-oscar
> http://django-oscar.readthedocs.org/en/latest/
> https://twitter.com/django_oscar
> ---
> You received this message because you are subscribed to the Google
> Groups "django-oscar" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-oscar...@googlegroups.com
> <mailto:django-oscar...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-oscar.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-oscar/ff462f96-d3ae-4d8a-9fc3-32ce098fa5da%40googlegroups.com
> <https://groups.google.com/d/msgid/django-oscar/ff462f96-d3ae-4d8a-9fc3-32ce098fa5da%40googlegroups.com?utm_medium=email&utm_source=footer>.

shafiqu...@gmail.com

unread,
Jun 27, 2014, 10:27:24 AM6/27/14
to django...@googlegroups.com
Hello Maik,

I actually met a group of people in Dushanbe that were biking through Central Asia at the time. I didn't even know it was possible. I think they were a motorcycle club from France and Italy. If you are ever going through TJ, KG, KZ or UZ, do let me know. 

Thanks for the advice on how to do the overrides - it is way better than messing with the abstract model. For the postal codes, the reason I wanted to skip the validation is that many people I think are not familiar with their postal code, so they might enter too few or too many digits, even though the field is not required, which would trigger an error. I think I ended up just hiding the field anyways (though I was thinking to keep it visible, but optional, with no validation). I think I will try overriding the clean() method, since this is a better practice.

Cheers,
>      > <mailto:django-oscar+unsub...@googlegroups.com <javascript:>>.
Reply all
Reply to author
Forward
0 new messages