Abstract Models

Firstly, need to add basic models TimeStampedEditableModel is an abstract base class model, that provides self-updating created and modified fields. Write base class and put abstract=True in the Meta class. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, it’s fields will be added to those of the child class.

Example of TimeStampedEditableModel code:

#django_freeradius/base/models.py

from model_utils.fields import AutoCreatedField, AutoLastModifiedField

class TimeStampedEditableModel(models.Model):
    """
    An abstract base class model that provides self-updating
    ``created`` and ``modified`` fields.
    """
    created = AutoCreatedField(_('created'), editable=True)
    modified = AutoLastModifiedField(_('modified'), editable=True)

    class Meta:
        abstract = True

Include the TimeStampedEditableModel to the AbstractModel

Example:

#django_freeradius/base/models.py

class AbstractRadiusGroup(TimeStampedEditableModel):
    id = models.UUIDField(primary_key=True, db_column='id')
    group_name = models.CharField(
            verbose_name=_('groupname'), max_length=255, unique=True, db_column='groupname', db_index=True)
    priority = models.IntegerField(verbose_name=_('priority'), default=1)
    creation_date = models.DateField(verbose_name=_('creation date'), null=True, db_column='created_at')
    modification_date = models.DateField(
        verbose_name=_('modification date'), null=True, db_column='updated_at')
    notes = models.CharField(
        verbose_name=_('notes'), max_length=64, blank=True, null=True)

    class Meta:
        db_table = 'radiusgroup'
        verbose_name = _('radiusgroup')
        verbose_name_plural = _('radiusgroups')
        abstract = True

    def __str__(self):
        return self.group_name

Introduce a ModelAdmin for TimeStampedEditableAdmin

Example of code:

#django_freeradius/base/admin.py

from django.contrib.admin import ModelAdmin


class TimeStampedEditableAdmin(ModelAdmin):
    """
    ModelAdmin for TimeStampedEditableModel
    """

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = super(TimeStampedEditableAdmin, self).get_readonly_fields(request, obj)
        return readonly_fields + ('created', 'modified')


class AbstractRadiusGroupAdmin(TimeStampedEditableAdmin):
    pass

Creating a Reusable App

First, You have to install swapper. If you are publishing your reusable app as a Python package, be sure to add swapper to your project’s dependencies.You may also want to take a look at the Swapper Guide <https://github.com/wq/django-swappable-models>

Install swapper:

pip install swapper

In your reusable models use import swapper and add to Meta class swappable = swapper.swappable_setting(‘reusable_app’, ‘model’):

#django_freeradius/models.py

import swapper

from .base.models import (AbstractNas, AbstractRadiusAccounting,
                          AbstractRadiusCheck, AbstractRadiusGroup,
                          AbstractRadiusGroupCheck, AbstractRadiusGroupReply,
                          AbstractRadiusGroupUsers,
                          AbstractRadiusPostAuthentication,
                          AbstractRadiusReply, AbstractRadiusUserGroup)


class RadiusGroup(AbstractRadiusGroup):

    class Meta(AbstractRadiusGroup.Meta):
        abstract = False
        swappable = swapper.swappable_setting('django_freeradius', 'RadiusGroup')

Migrations

Swapper can also be used in Django 1.7+ migration scripts to facilitate dependency ordering and foreign key references. To use this feature in your library, generate a migration script with makemigrations and make the following changes:

#django_freeradius/migrations

import swapper

class Migration(migrations.Migration):

    initial = True

 dependencies = [
     swapper.dependency('django_freeradius', 'RadiusReply'),
     swapper.dependency('django_freeradius', 'RadiusCheck'),
 ]

 operations = [
     migrations.CreateModel(
         name='Nas',
         fields=[
             ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
             ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
             ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
             ('nas_name', models.CharField(db_column='nasname', db_index=True, help_text='NAS Name (or IP address)', max_length=128, unique=True, verbose_name='nas name')),
             ('short_name', models.CharField(db_column='shortname', max_length=32, verbose_name='short name')),
             ('type', models.CharField(max_length=30, verbose_name='type')),
             ('secret', models.CharField(help_text='Shared Secret', max_length=60, verbose_name='secret')),
             ('ports', models.IntegerField(blank=True, null=True, verbose_name='ports')),
             ('community', models.CharField(blank=True, max_length=50, null=True, verbose_name='community')),
             ('description', models.CharField(max_length=200, null=True, verbose_name='description')),
             ('server', models.CharField(max_length=64, null=True, verbose_name='server')),
         ],
         options={
             'db_table': 'nas',
             'swappable': swapper.swappable_setting('django_freeradius', 'Nas'),
             'verbose_name': 'nas',
             'abstract': False,
             'verbose_name_plural': 'nas',
         },
     ),

Extends Models

The user of your app can override one or both models in their own app.

Example:

#sample_radius/models.py

from django.db import models
from django.utils.translation import ugettext_lazy as _

from django_freeradius.models import (AbstractNas, AbstractRadiusAccounting,
                                      AbstractRadiusCheck, AbstractRadiusGroup,
                                      AbstractRadiusGroupCheck, AbstractRadiusGroupReply,
                                      AbstractRadiusGroupUsers,
                                      AbstractRadiusPostAuthentication,
                                      AbstractRadiusReply, AbstractRadiusUserGroup)


class RadiusGroup(AbstractRadiusGroup):
    details = models.CharField(
            verbose_name=_('details'), max_length=64, blank=True, null=True)


class RadiusCheck(AbstractRadiusCheck):
    details = models.CharField(
            verbose_name=_('details'), max_length=64, blank=True, null=True)

Add swapper.load_model() to sample_radius/admin.py. Example:

from django.contrib import admin

import swapper
from django_freeradius.admin import (AbstractNasAdmin,
                                     AbstractRadiusAccountingAdmin,
                                     AbstractRadiusCheckAdmin,
                                     AbstractRadiusGroupAdmin,
                                     AbstractRadiusGroupCheckAdmin,
                                     AbstractRadiusGroupReplyAdmin,
                                     AbstractRadiusGroupUsersAdmin,
                                     AbstractRadiusPostAuthenticationAdmin,
                                     AbstractRadiusReplyAdmin,
                                     AbstractRadiusUserGroupAdmin)

RadiusGroupReply = swapper.load_model("django_freeradius", "RadiusGroupReply")
RadiusGroupCheck = swapper.load_model("django_freeradius", "RadiusGroupCheck")
RadiusGroupUsers = swapper.load_model("django_freeradius", "RadiusGroupUsers")
RadiusUserGroup = swapper.load_model("django_freeradius", "RadiusUserGroup")
RadiusReply = swapper.load_model("django_freeradius", "RadiusReply")
RadiusCheck = swapper.load_model("django_freeradius", "RadiusCheck")
RadiusPostAuthentication = swapper.load_model("django_freeradius", "RadiusPostAuthentication")
Nas = swapper.load_model("django_freeradius", "Nas")
RadiusAccounting = swapper.load_model("django_freeradius", "RadiusAccounting")
RadiusGroup = swapper.load_model("django_freeradius", "RadiusGroup")


@admin.register(RadiusGroup)
class RadiusGroupAdmin(AbstractRadiusGroupAdmin):
    model = RadiusGroup

Update Settings

Update the settings to trigger the swapper:

#django_freeradius/tests/settings.py

if os.environ.get('SAMPLE_APP', False):
        INSTALLED_APPS.append('sample_radius')
        DJANGO_FREERADIUS_RADIUSREPLY_MODEL = "sample_radius.RadiusReply"
        DJANGO_FREERADIUS_RADIUSGROUPREPLY_MODEL = "sample_radius.RadiusGroupReply"
        DJANGO_FREERADIUS_RADIUSCHECK_MODEL = "sample_radius.RadiusCheck"
        DJANGO_FREERADIUS_RADIUSGROUPCHECK_MODEL = "sample_radius.RadiusGroupCheck"
        DJANGO_FREERADIUS_RADIUSACCOUNTING_MODEL = "sample_radius.RadiusAccounting"
        DJANGO_FREERADIUS_NAS_MODEL = "sample_radius.Nas"
        DJANGO_FREERADIUS_RADIUSGROUPUSERS_MODEL = "sample_radius.RadiusGroupUsers"
        DJANGO_FREERADIUS_RADIUSUSERGROUP_MODEL = "sample_radius.RadiusUserGroup"
        DJANGO_FREERADIUS_RADIUSPOSTAUTHENTICATION_MODEL = "sample_radius.RadiusPostAuthentication"
        DJANGO_FREERADIUS_RADIUSGROUP_MODEL = "sample_radius.RadiusGroup"