Skip to content

Commit 2992104

Browse files
authored
Merge pull request #7 from 4teamwork/jch/TI-2893
Implement custom fields
2 parents 1c9dd2c + 615bd9d commit 2992104

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2824
-12
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Add desired app to `INSTALLED_APPS` in your Django project.
1616
Available apps:
1717
```
1818
django_features.system_message
19+
django_features.custom_fields
1920
```
2021

2122
## Configuration
@@ -30,6 +31,27 @@ class Base(BaseConfiguration):
3031
...
3132
```
3233

34+
### Custom Fields
35+
36+
To use all features of the `django_features.custom_fields` app, the following steps are required:
37+
38+
Add the `django_features.custom_fields.routers.custom_field_router` to your `ROOT_URLCONF`. For example:
39+
40+
```
41+
path("api/", include(custom_field_router.urls)),
42+
```
43+
44+
#### Models
45+
46+
Your models should inherit from `django_features.custom_fields.models.CustomFieldBaseModel`.
47+
48+
#### Querysets
49+
50+
Your querysets should inherit from `django_features.custom_fields.models.CustomFieldModelBaseManager`.
51+
52+
#### Serializers
53+
54+
Your serializers should inherit from `django_features.custom_fields.serializers.CustomFieldBaseModelSerializer`.
3355

3456
### System Message
3557

app/admin.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from django.contrib import admin
2+
3+
from app import models
4+
5+
6+
@admin.register(models.Address)
7+
class AddressAdmin(admin.ModelAdmin):
8+
list_display = ("id", "__str__")
9+
list_display_links = ("id", "__str__")
10+
search_fields = ("city", "country", "street", "zip_code")
11+
12+
13+
@admin.register(models.Person)
14+
class PersonAdmin(admin.ModelAdmin):
15+
list_display = ("id", "__str__", "email")
16+
list_display_links = ("id", "__str__")
17+
search_fields = ("email", "firstname", "lastname")

app/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class DummyAppConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "app"

app/management/__init__.py

Whitespace-only changes.

app/management/commands/__init__.py

Whitespace-only changes.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import random
2+
from typing import Any
3+
4+
from django.contrib.contenttypes.models import ContentType
5+
from django.core.management import BaseCommand
6+
7+
from app.models import Person
8+
from django_features.custom_fields.models import CustomField
9+
from django_features.custom_fields.models import CustomValue
10+
11+
12+
class Command(BaseCommand):
13+
NUMBER_OF_CUSTOM_FIELDS = 100
14+
NUMBER_OF_OBJECTS = 1000
15+
16+
def _value_for_type(self, field_type: str) -> Any:
17+
match field_type:
18+
case CustomField.FIELD_TYPES.CHAR:
19+
return "custom char field"
20+
case CustomField.FIELD_TYPES.TEXT:
21+
return "custom text field\nsecond line"
22+
case CustomField.FIELD_TYPES.INTEGER:
23+
return 123456789
24+
case CustomField.FIELD_TYPES.DATE:
25+
return "2025-01-01"
26+
case CustomField.FIELD_TYPES.DATETIME:
27+
return "2025-01-01T00:00:00Z"
28+
case CustomField.FIELD_TYPES.BOOLEAN:
29+
return random.choice([True, False])
30+
31+
def handle(self, *args: Any, **options: Any) -> None:
32+
custom_fields = []
33+
custom_values = []
34+
objects = []
35+
36+
field_type = 0
37+
person_c = ContentType.objects.get_for_model(Person)
38+
print("Add objects")
39+
for i in range(self.NUMBER_OF_OBJECTS):
40+
objects.append(Person(firstname=f"Firstname {i}", lastname=f"Lastname {i}"))
41+
42+
print("Add custom fields")
43+
for i in range(self.NUMBER_OF_CUSTOM_FIELDS):
44+
custom_fields.append(
45+
CustomField(
46+
content_type=person_c,
47+
field_type=CustomField.TYPE_CHOICES[field_type][0],
48+
identifier=f"custom_field_{i}",
49+
label=f"Custom field {i}",
50+
)
51+
)
52+
field_type = (
53+
field_type + 1 if field_type < len(CustomField.TYPE_CHOICES) - 1 else 0
54+
)
55+
56+
print("Add custom values")
57+
for i in range(self.NUMBER_OF_OBJECTS):
58+
for c in range(self.NUMBER_OF_CUSTOM_FIELDS):
59+
field = custom_fields[c]
60+
custom_values.append(
61+
CustomValue(
62+
field=field, value=self._value_for_type(field.field_type)
63+
)
64+
)
65+
66+
print("Execute bulk create")
67+
CustomField.objects.bulk_create(custom_fields)
68+
CustomValue.objects.bulk_create(custom_values)
69+
Person.objects.bulk_create(objects)
70+
71+
print("Set many2many relations")
72+
for i in range(self.NUMBER_OF_OBJECTS):
73+
for c in range(self.NUMBER_OF_CUSTOM_FIELDS):
74+
field = custom_fields[c]
75+
objects[i].custom_values.add(CustomValue.objects.filter(field=field)[i])

app/migrations/0001_initial.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Generated by Django 4.2.23 on 2025-10-09 05:44
2+
3+
import django.db.models.deletion
4+
import django_extensions.db.fields
5+
from django.db import migrations
6+
from django.db import models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
initial = True
12+
13+
dependencies = [
14+
("contenttypes", "0002_remove_content_type_name"),
15+
("custom_fields", "0001_initial"),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name="Person",
21+
fields=[
22+
(
23+
"id",
24+
models.BigAutoField(
25+
auto_created=True,
26+
primary_key=True,
27+
serialize=False,
28+
verbose_name="ID",
29+
),
30+
),
31+
(
32+
"created",
33+
django_extensions.db.fields.CreationDateTimeField(
34+
auto_now_add=True, verbose_name="created"
35+
),
36+
),
37+
(
38+
"modified",
39+
django_extensions.db.fields.ModificationDateTimeField(
40+
auto_now=True, verbose_name="modified"
41+
),
42+
),
43+
("firstname", models.CharField(verbose_name="firstname")),
44+
(
45+
"lastname",
46+
models.CharField(blank=True, null=True, verbose_name="lastname"),
47+
),
48+
(
49+
"email",
50+
models.EmailField(
51+
blank=True, max_length=254, null=True, verbose_name="email"
52+
),
53+
),
54+
(
55+
"custom_values",
56+
models.ManyToManyField(
57+
blank=True,
58+
to="custom_fields.customvalue",
59+
verbose_name="Benutzerdefinierte Werte",
60+
),
61+
),
62+
],
63+
options={
64+
"verbose_name": "Person",
65+
"verbose_name_plural": "Persons",
66+
},
67+
),
68+
migrations.CreateModel(
69+
name="Address",
70+
fields=[
71+
(
72+
"id",
73+
models.BigAutoField(
74+
auto_created=True,
75+
primary_key=True,
76+
serialize=False,
77+
verbose_name="ID",
78+
),
79+
),
80+
(
81+
"created",
82+
django_extensions.db.fields.CreationDateTimeField(
83+
auto_now_add=True, verbose_name="created"
84+
),
85+
),
86+
(
87+
"modified",
88+
django_extensions.db.fields.ModificationDateTimeField(
89+
auto_now=True, verbose_name="modified"
90+
),
91+
),
92+
("city", models.CharField(blank=True, verbose_name="city")),
93+
("country", models.CharField(blank=True, verbose_name="country")),
94+
("street", models.CharField(blank=True, verbose_name="street")),
95+
("zip_code", models.CharField(blank=True, verbose_name="postal code")),
96+
("target_id", models.PositiveIntegerField(blank=True, null=True)),
97+
(
98+
"custom_values",
99+
models.ManyToManyField(
100+
blank=True,
101+
to="custom_fields.customvalue",
102+
verbose_name="Benutzerdefinierte Werte",
103+
),
104+
),
105+
(
106+
"target_type",
107+
models.ForeignKey(
108+
blank=True,
109+
null=True,
110+
on_delete=django.db.models.deletion.CASCADE,
111+
to="contenttypes.contenttype",
112+
),
113+
),
114+
],
115+
options={
116+
"verbose_name": "Address",
117+
"verbose_name_plural": "Addresses",
118+
},
119+
),
120+
]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Generated by Django 4.2.23 on 2025-10-14 12:28
2+
3+
import django.db.models.deletion
4+
import django_extensions.db.fields
5+
from django.db import migrations
6+
from django.db import models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
("app", "0001_initial"),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="PersonType",
18+
fields=[
19+
(
20+
"id",
21+
models.BigAutoField(
22+
auto_created=True,
23+
primary_key=True,
24+
serialize=False,
25+
verbose_name="ID",
26+
),
27+
),
28+
(
29+
"created",
30+
django_extensions.db.fields.CreationDateTimeField(
31+
auto_now_add=True, verbose_name="created"
32+
),
33+
),
34+
(
35+
"modified",
36+
django_extensions.db.fields.ModificationDateTimeField(
37+
auto_now=True, verbose_name="modified"
38+
),
39+
),
40+
("title", models.CharField(max_length=255, verbose_name="title")),
41+
],
42+
options={
43+
"verbose_name": "Person type",
44+
"verbose_name_plural": "Person types",
45+
},
46+
),
47+
migrations.AddField(
48+
model_name="person",
49+
name="person_type",
50+
field=models.ForeignKey(
51+
blank=True,
52+
null=True,
53+
on_delete=django.db.models.deletion.SET_NULL,
54+
to="app.persontype",
55+
verbose_name="Type",
56+
),
57+
),
58+
]

app/migrations/__init__.py

Whitespace-only changes.

app/migrations/max_migration.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0002_add_person_type_and_relation

0 commit comments

Comments
 (0)