Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ A collection of fearures used in our Django-based web applications

[Changelog](CHANGELOG.md)

## Installation
# Installation

``` bash
pip install ftw-django-features
```

## Usage
# Usage

Add desired app to `INSTALLED_APPS` in your Django project.

Expand All @@ -19,7 +19,7 @@ django_features.system_message
django_features.custom_fields
```

## Configuration
# Configuration

If you want to use `django_features`, your base configuration class should inherit from `django_features.settings.BaseConfiguration`.

Expand All @@ -31,7 +31,7 @@ class Base(BaseConfiguration):
...
```

### Custom Fields
## Custom Fields

To use all features of the `django_features.custom_fields` app, the following steps are required:

Expand All @@ -41,24 +41,32 @@ Add the `django_features.custom_fields.routers.custom_field_router` to your `ROO
path("api/", include(custom_field_router.urls)),
```

#### Models
### Create your own custom field and value models

Your models should inherit from `django_features.custom_fields.models.CustomFieldBaseModel`.
1. You need to create a custom field model and a custom value model.
2. Your custom field model should inherit from `django_features.custom_fields.models.field.AbstractBaseCustomField`.
3. Your custom value model should inherit from `django_features.custom_fields.models.value.AbstractBaseCustomValue`.

#### Swappable
### Configuration

You can swap the models used by the `django_features.custom_fields` app by setting the `CUSTOM_FIELD_MODEL` or `CUSTOM_FIELD_VALUE_MODEL` setting.
The swapped models should inherit from `django_features.custom_fields.models.field.AbstractBaseCustomField` or `django_features.custom_fields.models.value.AbstractBaseCustomValue`.
- You can configure the models used by the `django_features.custom_fields` app by setting the `CUSTOM_FIELD_MODEL` or `CUSTOM_FIELD_VALUE_MODEL` setting.
- The swapped models should inherit from `django_features.custom_fields.models.field.AbstractBaseCustomField` or `django_features.custom_fields.models.value.AbstractBaseCustomValue`.

### Models with custom values

1. Your models with custom values should inherit from `django_features.custom_fields.models.CustomFieldBaseModel`.
2. Your models should have a relation to the custom value model. For example:
- `custom_values = models.ManyToManyField(blank=True, to=CustomValue, verbose_name=_("Benutzerdefinierte Werte"))`

#### Querysets

Your querysets should inherit from `django_features.custom_fields.models.CustomFieldModelBaseManager`.
Your querysets for the models with custom values should inherit from `django_features.custom_fields.models.CustomFieldModelBaseManager`.

#### Serializers

Your serializers should inherit from `django_features.custom_fields.serializers.CustomFieldBaseModelSerializer`.
Your serializers for the models with custom values should inherit from `django_features.custom_fields.serializers.CustomFieldBaseModelSerializer`.

### System Message
## System Message

If you want to use `django_features.system_message`, your base configuration class should inherit from `django_features.system_message.settings.SystemMessageConfigurationMixin`.

Expand All @@ -85,15 +93,15 @@ Add the `django_features.system_message.routers.system_message_router` to your `
path("api/", include(system_message_router.urls)),
```

## Development
# Development

Installing dependencies, assuming you have poetry installed:

``` bash
poetry install
```

## Release
# Release

This package uses towncrier to manage the changelog, and to introduce new changes, a file with a concise title and a brief explanation of what the change accomplishes should be created in the `changes` directory, with a suffix indicating whether the change is a feature, bugfix, or other.

Expand Down
File renamed without changes.
30 changes: 30 additions & 0 deletions app/custom_field/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.contrib import admin
from modeltranslation.admin import TranslationAdmin

from app.custom_field import models
from django_features.custom_fields.admin import BaseAdmin
from django_features.custom_fields.admin import CustomFieldBaseAdmin


@admin.register(models.CustomField)
class CustomFieldAdmin(BaseAdmin, CustomFieldBaseAdmin, TranslationAdmin):
list_display = ["id", "identifier", "__str__", "field_type", "filterable"]
list_display_links = (
"id",
"identifier",
"__str__",
)
list_filter = (
"choice_field",
"content_type",
"editable",
"field_type",
"filterable",
)
search_fields = ("label", "identifier")


@admin.register(models.CustomValue)
class ValueAdmin(BaseAdmin, TranslationAdmin):
list_display = ["id", "__str__"]
search_fields = ("label", "value", "field__label", "field__identifier")
6 changes: 6 additions & 0 deletions app/custom_field/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class CustomFieldsConfig(AppConfig):
name = "app.custom_field"
label = "custom_field"
200 changes: 200 additions & 0 deletions app/custom_field/migrations/0001_inherit_from_custom_base_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# Generated by Django 4.2.23 on 2026-02-24 14:37

import django.db.models.deletion
import django_extensions.db.fields
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

initial = True

dependencies = [
("contenttypes", "0002_remove_content_type_name"),
]

operations = [
migrations.CreateModel(
name="CustomField",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name="created"
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name="modified"
),
),
(
"allow_blank",
models.BooleanField(
default=True, verbose_name="Leeren String erlauben"
),
),
(
"allow_null",
models.BooleanField(
default=True, verbose_name="Leere Werte erlauben"
),
),
(
"choice_field",
models.BooleanField(default=False, verbose_name="Auswahlfeld"),
),
(
"default",
models.JSONField(
blank=True, null=True, verbose_name="Standardwert"
),
),
(
"editable",
models.BooleanField(default=True, verbose_name="Editierbar"),
),
(
"external_key",
models.CharField(
blank=True, null=True, verbose_name="Externer Key"
),
),
(
"field_type",
models.CharField(
choices=[
("CHAR", "Text (einzeilig)"),
("TEXT", "Text (mehrzeilig)"),
("DATE", "Datum"),
("DATETIME", "Datum und Zeit"),
("INTEGER", "Zahl (Ganzzahl)"),
("BOOLEAN", "Checkbox"),
],
verbose_name="Feldtyp",
),
),
(
"hidden",
models.BooleanField(default=False, verbose_name="Ausblenden"),
),
("identifier", models.SlugField(max_length=64, unique=True)),
(
"filterable",
models.BooleanField(
default=False, verbose_name="Als Filter anbieten"
),
),
("label", models.CharField(verbose_name="Name")),
("label_de", models.CharField(null=True, verbose_name="Name")),
("label_en", models.CharField(null=True, verbose_name="Name")),
("label_fr", models.CharField(null=True, verbose_name="Name")),
("multiple", models.BooleanField(default=False, verbose_name="Liste")),
(
"order",
models.PositiveSmallIntegerField(
default=0, verbose_name="Reihenfolge"
),
),
(
"required",
models.BooleanField(default=False, verbose_name="Erforderlich"),
),
("type_id", models.PositiveIntegerField(blank=True, null=True)),
(
"content_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="contenttypes.contenttype",
),
),
(
"type_content_type",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="customfield_set_for_type",
to="contenttypes.contenttype",
),
),
],
options={
"verbose_name": "Benutzerdefiniertes Feld",
"verbose_name_plural": "Benutzerdefinierte Felder",
"ordering": ["order", "created"],
},
),
migrations.CreateModel(
name="CustomValue",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name="created"
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name="modified"
),
),
(
"order",
models.PositiveIntegerField(default=0, verbose_name="Reihenfolge"),
),
(
"label",
models.CharField(blank=True, null=True, verbose_name="Label"),
),
(
"label_de",
models.CharField(blank=True, null=True, verbose_name="Label"),
),
(
"label_en",
models.CharField(blank=True, null=True, verbose_name="Label"),
),
(
"label_fr",
models.CharField(blank=True, null=True, verbose_name="Label"),
),
("value", models.JSONField(blank=True, null=True, verbose_name="Wert")),
(
"field",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="values",
to="custom_field.customfield",
verbose_name="Feld",
),
),
],
options={
"verbose_name": "Benutzerdefinierter Wert",
"verbose_name_plural": "Benutzerdefinierte Werte",
"ordering": ["order", "created"],
},
),
]
Empty file.
1 change: 1 addition & 0 deletions app/custom_field/migrations/max_migration.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0001_inherit_from_custom_base_model
5 changes: 5 additions & 0 deletions app/custom_field/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__all__ = ["CustomField", "CustomValue"]


from .field import CustomField
from .value import CustomValue
30 changes: 30 additions & 0 deletions app/custom_field/models/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.utils.translation import gettext_lazy as _

from app.custom_field.models import CustomField
from app.custom_field.models import CustomValue
from django_features.custom_fields.models import CustomFieldBaseModel
from django_features.custom_fields.models import CustomFieldTypeBaseModel


class CustomTypeBaseModel(CustomFieldTypeBaseModel):
custom_fields = GenericRelation(
CustomField,
object_id_field="type_id",
content_type_field="type_content_type",
)

class Meta:
abstract = True


class CustomBaseModel(CustomFieldBaseModel):
custom_values = models.ManyToManyField(
blank=True,
to=CustomValue,
verbose_name=_("Benutzerdefinierte Werte"),
)

class Meta:
abstract = True
10 changes: 10 additions & 0 deletions app/custom_field/models/field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.utils.translation import gettext_lazy as _

from django_features.custom_fields.models.field import AbstractBaseCustomField


class CustomField(AbstractBaseCustomField):
class Meta:
verbose_name = _("Benutzerdefiniertes Feld")
verbose_name_plural = _("Benutzerdefinierte Felder")
ordering = ["order", "created"]
Loading