pythondjangodjango-oscar

How to alter Fields in Django and Oscar


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!


Solution

  • 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'>