1

I am making a web application with django-oscar, and I would like to make the user field required in the Partner Model.

My first approach was to override the "user" Field in the Partner model, but then I read that you cannot override Fields in Django.

So, how does one alter a field in Oscar if we can't override it?

Thanks!

kroppian
  • 127
  • 1
  • 6

1 Answers1

3

The docs aren't very clear, but fortunately this kind of model overriding is well-supported in Oscar thanks to the way it's been engineered.

But instead of sub-classing the oscar.apps.partner.Partner model and overriding the field you need to make your own myproject.partner app.

The key thing to note is all the models in Oscar are implemented as abstract models. Then Oscar checks to see if you've overridden any of the framework apps, if not Oscar will define a concrete model from its abstract ones. If you have overridden a framework app though it will first try to load your concrete model, then fallback to it's own models for any you haven't touched.

See the docs here:
https://django-oscar.readthedocs.org/en/releases-1.0/topics/customisation.html#fork-the-oscar-app

First run this:

$ ./manage.py oscar_fork_app partner myproject/

which will create the skeleton of the overridden app for you as myproject.partner

then edit your settings:

# settings.py

from oscar import get_core_apps
# ...
INSTALLED_APPS = [
    # all your non-Oscar apps
] + get_core_apps(['myproject.partner'])

and in myproject/partner/models.py:

from django.db import models

from oscar.apps.partner.abstract_models import AbstractPartner


class Partner(AbstractPartner):
    user = models.ForeignKey(...customised here...)


from oscar.apps.partner.models import *

Since you imported them at the bottom them the partner.StockRecord and partner.StockAlert models will come from Oscar like normal, but it will use your customised Partner model automatically everywhere.

If you want to use the abstracted class loading apparatus that Oscar uses you can do this (and confirm your override is working):

In [1]: from oscar.core.loading import get_class

In [2]: Partner = get_class('partner.models', 'Partner')

In [3]: print Partner
<class 'myproject.partner.models.Partner'>

In [4]: StockAlert = get_class('partner.models', 'StockAlert')

In [5]: print StockAlert
<class 'oscar.apps.partner.models.StockAlert'>
Anentropic
  • 32,188
  • 12
  • 99
  • 147
  • 1
    Thanks for the response! I have forked the `oscar.apps.partner.Partner` app as you instructed, and I get the error message `django.core.exceptions.FieldError: Local field 'users' in class 'Partner' clashes with field of similar name from base class 'AbstractPartner'` Does this mean that the abstract partner model is being built into a concrete model before my forked partner model is read? – kroppian Mar 17 '15 at 22:21
  • it seems unfortunately Django doesn't allow this even for abstract models, so in addition to above you're going to have to copy and paste from the AbstractPartner model rather than inherit from it. What change do you need to make to the `users` field though? – Anentropic Mar 18 '15 at 10:07
  • Hmmmm... that's what I feared. I want to require all partners be linked to a user. – kroppian Mar 18 '15 at 14:11
  • in that case... since its an M2M the `null=True` attr is ignored and the `blank=True` does not mean anything at db schema level, these attrs just inform Django model validation (python)... in which case you can probably do something in model `__init__` method to set those attrs and tell Django to treat the field as required – Anentropic Mar 18 '15 at 15:23