Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ac18348
Add property to get all custom fields
Joel-Luca Oct 21, 2025
5a4c2d3
Add uuid related field for serializer
Joel-Luca Oct 1, 2025
7bdfab6
Add pretty json field to django features
Joel-Luca Oct 1, 2025
20c958d
Add validation mixin for mapping field
Joel-Luca Oct 1, 2025
1af080c
Add mapping field
Joel-Luca Oct 1, 2025
64ec0d8
Test ModelFieldMapping and ValidationMixin
Joel-Luca Oct 14, 2025
efcddb1
Add json and mapping field to additional constance fields
Joel-Luca Oct 1, 2025
6309d83
Implement mapping serializer for model import
Joel-Luca Oct 9, 2025
1f5eaca
Add the option to define the serializer as write only serializer
Joel-Luca Oct 14, 2025
36c4c1c
Add the option to define the filtered field and exclude other field c…
Joel-Luca Oct 14, 2025
21029bc
Use ChoiceIdField for choice fields
Joel-Luca Oct 21, 2025
1acd718
Adapt to representation for ChoiceIdField to return choice data
Joel-Luca Oct 21, 2025
bc6ca12
Fix choice serializer value fields
Joel-Luca Oct 21, 2025
9743b6c
Adapt serializer tests
Joel-Luca Oct 21, 2025
b0ba986
Update dummy app with example models for development
Joel-Luca Oct 9, 2025
fa73b6c
Test mapping serializer
Joel-Luca Oct 9, 2025
d3465d2
Add changelog entry for nested mapping import
Joel-Luca Oct 9, 2025
aab76fc
Add choices property
Joel-Luca Oct 22, 2025
6b53fdf
Adapt update function to prevent db access during transaction
Joel-Luca Oct 22, 2025
a7b5b40
Remove and replace multiple choice with choice field
Joel-Luca Oct 22, 2025
98ee943
Rename text to label
Joel-Luca Oct 22, 2025
fc11b76
Handle programming error when the apps or the db are not ready
Joel-Luca Oct 22, 2025
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
11 changes: 9 additions & 2 deletions app/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
class AddressAdmin(admin.ModelAdmin):
list_display = ("id", "__str__")
list_display_links = ("id", "__str__")
search_fields = ("city", "country", "street", "zip_code")
search_fields = ("id", "city", "country", "street", "zip_code")


@admin.register(models.Municipality)
class MunicipalityAdmin(admin.ModelAdmin):
list_display = ("id", "__str__")
list_display_links = ("id", "__str__")
search_fields = ("id", "title")


@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin):
list_display = ("id", "__str__", "email")
list_display_links = ("id", "__str__")
search_fields = ("email", "firstname", "lastname")
search_fields = ("id", "email", "firstname", "lastname")
59 changes: 59 additions & 0 deletions app/migrations/0003_add_model_municipality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Generated by Django 4.2.23 on 2025-10-09 08:55

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


class Migration(migrations.Migration):

dependencies = [
("app", "0002_add_person_type_and_relation"),
]

operations = [
migrations.CreateModel(
name="Municipality",
fields=[
(
"id",
models.BigAutoField(
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"
),
),
("title", models.CharField(unique=True, verbose_name="title")),
],
options={
"verbose_name": "Municipality",
"verbose_name_plural": "Municipalities",
},
),
migrations.AddField(
model_name="person",
name="place_of_residence",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="persons",
to="app.municipality",
verbose_name="Place of residence",
),
),
]
19 changes: 19 additions & 0 deletions app/migrations/0004_add_external_uid_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.23 on 2025-10-09 10:54

from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

dependencies = [
("app", "0003_add_model_municipality"),
]

operations = [
migrations.AddField(
model_name="address",
name="external_uid",
field=models.UUIDField(null=True, verbose_name="external uid"),
),
]
2 changes: 1 addition & 1 deletion app/migrations/max_migration.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0002_add_person_type_and_relation
0004_add_external_uid_field
3 changes: 2 additions & 1 deletion app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__all__ = ["Address", "Person", "PersonType"]
__all__ = ["Address", "Municipality", "Person", "PersonType"]

from .address import Address
from .municipality import Municipality
from .person import Person
from .person import PersonType
1 change: 1 addition & 0 deletions app/models/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Address(CustomFieldBaseModel):
city = models.CharField(verbose_name="city", blank=True)
country = models.CharField(verbose_name="country", blank=True)
street = models.CharField(verbose_name="street", blank=True)
external_uid = models.UUIDField(verbose_name="external uid", null=True)
zip_code = models.CharField(verbose_name="postal code", blank=True)

target = GenericForeignKey("target_type", "target_id")
Expand Down
13 changes: 13 additions & 0 deletions app/models/municipality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.db import models
from django_extensions.db.models import TimeStampedModel


class Municipality(TimeStampedModel):
title = models.CharField(verbose_name="title", unique=True)

class Meta:
verbose_name = "Municipality"
verbose_name_plural = "Municipalities"

def __str__(self) -> str:
return self.title
8 changes: 8 additions & 0 deletions app/models/person.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ class Person(CustomFieldBaseModel):
on_delete=models.SET_NULL,
verbose_name="Type",
)
place_of_residence = models.ForeignKey(
"Municipality",
on_delete=models.PROTECT,
blank=True,
null=True,
verbose_name="Place of residence",
related_name="persons",
)

class Meta:
verbose_name = "Person"
Expand Down
11 changes: 11 additions & 0 deletions app/serializers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from constance import config

from django_features.serializers import MappingSerializer


class BaseMappingSerializer(MappingSerializer):
@property
def mapping(self) -> dict[str, dict[str, Any]]:
return config.MODEL_MAPPING_FIELD
15 changes: 15 additions & 0 deletions app/serializers/person.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from typing import Any

from constance import config

from app.models import Person
from app.serializers import BaseMappingSerializer
from django_features.custom_fields.serializers import CustomFieldBaseModelSerializer


Expand All @@ -10,3 +15,13 @@ class Meta:
"firstname",
"lastname",
]


class PersonMappingSerializer(BaseMappingSerializer):
class Meta:
model = Person
fields = "__all__"

@property
def mapping(self) -> dict[str, dict[str, Any]]:
return config.MODEL_MAPPING_FIELD
23 changes: 20 additions & 3 deletions app/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,36 @@ def STATIC_URL(self) -> str:

CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"

@property
def CONSTANCE_ADDITIONAL_FIELDS(self) -> dict:
config = super().CONSTANCE_ADDITIONAL_FIELDS
return {**config}

@property
def CONSTANCE_CONFIG(self) -> dict:
config = super().CONSTANCE_CONFIG
return {**config, "TEST": (False, "Test add addidional constances", bool)}
return {
**config,
"JSON_FIELD": (
{"key": {"nested_key": "value"}},
"Test formated field",
"json",
),
"MODEL_MAPPING_FIELD": (
{},
"Test model field mapping",
"model_field_mapping",
),
}

@property
def CONSTANCE_CONFIG_FIELDSETS(self) -> dict:
config = super().CONSTANCE_CONFIG_FIELDSETS
return {
**config,
"Miscellaneous": {
"fields": ("TEST",),
"collapse": True,
"fields": ("JSON_FIELD", "MODEL_MAPPING_FIELD"),
"collapse": False,
},
}

Expand Down
20 changes: 11 additions & 9 deletions app/tests/custom_fields/test_custom_field_base_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,12 @@ def test_custom_field_base_manager_annotate_choice_value(self) -> None:
field_type=CustomField.FIELD_TYPES.DATE,
choice_field=True,
)
choice_1 = CustomValueFactory(field=field, text_de="Choice 1", value="choice_1")
choice_1 = CustomValueFactory(
field=field, label_de="Choice 1", value="choice_1"
)
self.person.custom_values.add(choice_1)
self.assertEqual(
{"id": choice_1.id, "text": "Choice 1", "value": "choice_1"},
{"id": choice_1.id, "label": "Choice 1", "value": "choice_1"},
Person.objects.first().choice_value,
)

Expand All @@ -147,23 +149,23 @@ def test_custom_field_base_manager_annotate_multiple_date_choice_value(
content_type=self.person_ct,
field_type=CustomField.FIELD_TYPES.DATE,
choice_field=True,
multiple_choice=True,
multiple=True,
)
choice_1 = CustomValueFactory(
field=field, text_de="Choice 1", value="2000-01-01"
field=field, label_de="Choice 1", value="2000-01-01"
)
choice_2 = CustomValueFactory(
field=field, text_de="Choice 2", value="2001-01-01"
field=field, label_de="Choice 2", value="2001-01-01"
)
choice_3 = CustomValueFactory(
field=field, text_de="Choice 3", value="2002-01-01"
field=field, label_de="Choice 3", value="2002-01-01"
)
self.person.custom_values.set([choice_1, choice_2, choice_3])
self.assertEqual(
[
{"id": choice_1.id, "text": "Choice 1", "value": "2000-01-01"},
{"id": choice_2.id, "text": "Choice 2", "value": "2001-01-01"},
{"id": choice_3.id, "text": "Choice 3", "value": "2002-01-01"},
{"id": choice_1.id, "label": "Choice 1", "value": "2000-01-01"},
{"id": choice_2.id, "label": "Choice 2", "value": "2001-01-01"},
{"id": choice_3.id, "label": "Choice 3", "value": "2002-01-01"},
],
Person.objects.first().multiple_choice_value,
)
12 changes: 6 additions & 6 deletions app/tests/custom_fields/test_custom_field_base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def test_custom_field_base_model_set_choice_value(self) -> None:
field_type=CustomField.FIELD_TYPES.CHAR,
choice_field=True,
)
choice_1 = CustomValueFactory(field=field, text="Choice 1", value="choice_1")
choice_1 = CustomValueFactory(field=field, label="Choice 1", value="choice_1")

# we need to annotate the custom_field_keys manually or to fetch the person with the queryset again,
# because we created a new field
Expand All @@ -263,7 +263,7 @@ def test_custom_field_base_model_set_choice_value(self) -> None:

self.assertEqual(choice_1, self.person.choice_value)
self.assertEqual(
{"id": choice_1.id, "text": "Choice 1", "value": "choice_1"},
{"id": choice_1.id, "label": "Choice 1", "value": "choice_1"},
Person.objects.first().choice_value,
)

Expand All @@ -275,7 +275,7 @@ def test_custom_field_base_model_set_multiple_choice_value(
content_type=self.person_ct,
field_type=CustomField.FIELD_TYPES.DATE,
choice_field=True,
multiple_choice=True,
multiple=True,
)
choice_1 = CustomValueFactory(field=field, value="2000-01-01")
choice_2 = CustomValueFactory(field=field, value="2001-01-01")
Expand All @@ -301,9 +301,9 @@ def test_custom_field_base_model_set_multiple_choice_value(
)
self.assertEqual(
[
{"id": choice_1.id, "text": None, "value": "2000-01-01"},
{"id": choice_2.id, "text": None, "value": "2001-01-01"},
{"id": choice_3.id, "text": None, "value": "2002-01-01"},
{"id": choice_1.id, "label": None, "value": "2000-01-01"},
{"id": choice_2.id, "label": None, "value": "2001-01-01"},
{"id": choice_3.id, "label": None, "value": "2002-01-01"},
],
Person.objects.first().multiple_choice_value,
)
Loading