diff --git a/docs/source/general/abstract_models.rst b/docs/source/general/abstract_models.rst deleted file mode 100644 index 9d8d9e1..0000000 --- a/docs/source/general/abstract_models.rst +++ /dev/null @@ -1,244 +0,0 @@ -=============== -Abstract Models -=============== - -Firstly, we need to add basic models. TimeStampedEditableModel is an abstract base class model that provides self-updating -created and modified fields. If we write the 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, its fields will be added to those -of the child class. - -Example of TimeStampedEditableModel code: - -.. code-block:: python - - #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: - -.. code-block:: python - - #django_freeradius/base/models.py - - class BaseModel(TimeStampedEditableModel): - id = None - - class Meta: - abstract = True - - - class AbstractRadiusReply(BaseModel): - username = models.CharField(verbose_name=_('username'), - max_length=64, - db_index=True) - value = models.CharField(verbose_name=_('value'), max_length=253) - op = models.CharField(verbose_name=_('operator'), - max_length=2, - choices=RADOP_REPLY_TYPES, - default='=') - attribute = models.CharField(verbose_name=_('attribute'), max_length=64) - - class Meta: - db_table = 'radreply' - verbose_name = _('radius reply') - verbose_name_plural = _('radius replies') - abstract = True - - def __str__(self): - return self.username - - -Introduce a ModelAdmin for TimeStampedEditableAdmin ---------------------------------------------------- - -Example of code: - -.. code-block:: python - - - #django_freeradius/base/admin.py - - from django.contrib.admin import ModelAdmin - from openwisp_utils.admin import TimeReadonlyAdminMixin - - class TimeStampedEditableAdmin(TimeReadonlyAdminMixin, ModelAdmin): - pass - - - class AbstractRadiusReplyAdmin(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 -` - -Install swapper: - -.. code-block:: shell - - pip install swapper - - -In your reusable models, use ``import swapper`` and add to Meta class ``swappable = swapper.swappable_setting('reusable_app', 'model')``: - -.. code-block:: python - - #django_freeradius/models.py - - import swapper - - from .base.models import (AbstractNas, AbstractRadiusAccounting, - AbstractRadiusCheck, AbstractRadiusGroupCheck, - AbstractRadiusGroupReply, AbstractRadiusPostAuth, - AbstractRadiusReply, AbstractRadiusUserGroup) - - - class RadiusCheck(AbstractRadiusCheck): - class Meta(AbstractRadiusCheck.Meta): - abstract = False - swappable = swappable_setting('django_freeradius', 'RadiusCheck') - - -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: - -.. code-block:: python - - #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: - -.. code-block:: python - - #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, - AbstractRadiusGroupCheck, AbstractRadiusGroupReply, - AbstractRadiusPostAuth, - AbstractRadiusReply, AbstractRadiusUserGroup) - - - 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: - -.. code-block:: python - - from django.contrib import admin - - import swapper - from django_freeradius.admin import (AbstractNasAdmin, - AbstractRadiusAccountingAdmin, - AbstractRadiusCheckAdmin, - AbstractRadiusGroupCheckAdmin, - AbstractRadiusGroupReplyAdmin, - AbstractRadiusPostAuthAdmin, - AbstractRadiusReplyAdmin, - AbstractRadiusUserGroupAdmin) - - RadiusGroupReply = swapper.load_model("django_freeradius", "RadiusGroupReply") - RadiusGroupCheck = swapper.load_model("django_freeradius", "RadiusGroupCheck") - RadiusUserGroup = swapper.load_model("django_freeradius", "RadiusUserGroup") - RadiusReply = swapper.load_model("django_freeradius", "RadiusReply") - RadiusCheck = swapper.load_model("django_freeradius", "RadiusCheck") - RadiusPostAuth = swapper.load_model("django_freeradius", "RadiusPostAuth") - Nas = swapper.load_model("django_freeradius", "Nas") - RadiusAccounting = swapper.load_model("django_freeradius", "RadiusAccounting") - - - @admin.register(RadiusCheck) - class RadiusCheckAdmin(AbstractRadiusCheckAdmin): - pass - - ---------------- -Update Settings ---------------- - -Update the settings to trigger the swapper: - -.. code-block:: python - - #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_RADIUSUSERGROUP_MODEL = "sample_radius.RadiusUserGroup" - DJANGO_FREERADIUS_RADIUSPOSTAUTHENTICATION_MODEL = "sample_radius.RadiusPostAuth" diff --git a/docs/source/general/extend_django_freeradius.rst b/docs/source/general/extend_django_freeradius.rst new file mode 100644 index 0000000..8a16391 --- /dev/null +++ b/docs/source/general/extend_django_freeradius.rst @@ -0,0 +1,148 @@ +====================================== +Customizing django-freeradius +====================================== + +`django-freeeadius` provieds set of models, admin and API classes which can be imported, extende and hence customized by third party apps. + + +Extending models +---------------- +Apart from extending implemented models, `django_freeradius` also provides flexibility to extend abstract class models from `django-freeradius.base.models`. + +Example: + +.. code-block:: python + + #In sample_radius/models.py + + from django.db import models + from django_freeradius.base.models import AbstractRadiusCheck + + class RadiusCheck(AbstractRadiusCheck): + #modify/extend the default behavour here + + custom_field = models.TextField() + + +Extending admin +--------------- + +Similar to models, abstract admin classes from `django_freeradius.base.admin` can also be extended to avoid duplicate code. + +.. code-block:: python + + # In sample_radius/admin.py + + from django.contrib import admin + from .models import RadiusCheck + from django_freeradius.base.admin import AbstractRadiusAccountingAdmin + + class RadiusCheckAdmin(AbstractRadiusCheckAdmin): + model = RadiusCheck + + #modify/extend default behaviour here + + def __init__(self,*args,**kwargs): + #add your custom fields here + + self.fields.append('custom_field') + self.list_display.append('custom_field') + + super(AbstractRadiusCheckAdmin, self).__init__(*args, **kwargs) + + admin.site.register(RadiusCheck,RadiusCheckAdmin) + + + +Extending API views +------------------- + +Example of code: + +.. code-block:: python + + #django_freeradius/base/admin.py + + from django.contrib.admin import ModelAdmin + from openwisp_utils.admin import TimeReadonlyAdminMixin + + + class TimeStampedEditableAdmin(TimeReadonlyAdminMixin, ModelAdmin): + pass + + + class AbstractRadiusReplyAdmin(TimeStampedEditableAdmin): + list_display = ['username', 'attribute', 'op', + 'value', 'created', 'modified'] + autocomplete_fields = ['user'] + form = ModeSwitcherForm + fields = ['mode', + 'user', + 'username', + 'attribute', + 'op', + 'value', + 'created', + 'modified'] + + +Creating a Reusable App +----------------------- + +Install `swapper` to begin with this. If your reusable app is being published as a Python package, +be sure to add `swapper` to your project's dependencies. Learn more about `swapper` at the `Swapper Guide +` + +Install swapper: + +.. code-block:: shell + + pip install swapper + + +In your reusable models, use ``import swapper`` and add to Meta class ``swappable = swapper.swappable_setting('reusable_app', 'model')``: + +.. code-block:: python + + #django_freeradius/models.py + + from swapper import swappable_setting + + from .base.models import ( + AbstractNas, AbstractRadiusAccounting, AbstractRadiusBatch, AbstractRadiusCheck, AbstractRadiusGroup, + AbstractRadiusGroupCheck, AbstractRadiusGroupReply, AbstractRadiusPostAuth, AbstractRadiusReply, + AbstractRadiusToken, AbstractRadiusUserGroup, + ) + + + class RadiusCheck(AbstractRadiusCheck): + class Meta(AbstractRadiusCheck.Meta): + abstract = False + swappable = swappable_setting('django_freeradius', 'RadiusCheck') + + +.. note:: + 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: + +--------------- +Update Settings +--------------- + +Update the settings to trigger the swapper: + +.. code-block:: python + + #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_RADIUSUSERGROUP_MODEL = "sample_radius.RadiusUserGroup" + DJANGO_FREERADIUS_RADIUSPOSTAUTHENTICATION_MODEL = "sample_radius.RadiusPostAuth" diff --git a/docs/source/index.rst b/docs/source/index.rst index 71905dc..773e7c9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -34,7 +34,7 @@ Django-freeradius is part of the `OpenWISP project `_. /general/registration /general/social_login /general/api - /general/abstract_models + /general/extend_django_freeradius.rst /general/contributing /general/goals