-
Notifications
You must be signed in to change notification settings - Fork 2
Mk/conditional fields #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c7d1a16
2ef737f
817e3a5
97b33fc
0e0496e
5b22435
61ae3a6
87fad2a
2ae4b13
682fd44
38e24cb
6c43492
344060c
6824bc1
144d499
fed9d6e
5aa24f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| Customization Guide | ||
| ================= | ||
|
|
||
| This guide explains how to customize the Kompass application using configuration files and templates. | ||
|
|
||
| Configuration Files | ||
| ----------------- | ||
|
|
||
| The application uses two main configuration files: | ||
|
|
||
| * ``settings.toml``: Contains core application settings | ||
| * ``text.toml``: Contains customizable text content | ||
|
|
||
mariusrklein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| An example `settings.toml` can be found in `deploy/config/settings.toml`. For more details, see the following section. | ||
| settings.toml | ||
| ~~~~~~~~~~~~ | ||
|
|
||
| The ``settings.toml`` file contains all core configuration settings organized in sections: | ||
|
|
||
| .. code-block:: toml | ||
|
|
||
| [section] | ||
| name = "Your Section Name" | ||
| street = "Street Address" | ||
| town = "12345 Town" | ||
mariusrklein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # ... other section details | ||
|
|
||
| [LJP] | ||
| contribution_per_day = 25 | ||
| tax = 0.1 | ||
|
|
||
| [finance] | ||
| allowance_per_day = 22 | ||
| max_night_cost = 11 | ||
|
|
||
| Key sections include: | ||
|
|
||
| * ``[section]``: Organization details | ||
| * ``[LJP]``: Youth leadership program settings | ||
| * ``[finance]``: Financial configurations | ||
| * ``[misc]``: Miscellaneous application settings | ||
| * ``[mail]``: Email configuration | ||
| * ``[database]``: Database connection details | ||
mariusrklein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * ``[custom_model_fields]``: Customize visible model fields in admin interface (see below) | ||
|
|
||
| Customizing Model Fields | ||
| ~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| The ``[custom_model_fields]`` section in ``settings.toml`` allows you to customize which fields are visible in the admin interface: | ||
|
|
||
| .. code-block:: toml | ||
|
|
||
| [custom_model_fields] | ||
| # Format: applabel_modelname.fields = ['field1', 'field2'] | ||
| # applabel_modelname.exclude = ['field3', 'field4'] | ||
|
|
||
| # Example: Show only specific fields | ||
| members_emergencycontact.fields = ['prename', 'lastname', 'phone_number'] | ||
|
|
||
| # Example: Exclude specific fields | ||
| members_member.exclude = ['ticket_no', 'dav_badge_no'] | ||
|
|
||
| There are two ways to customize fields: | ||
|
|
||
| 1. Using ``fields``: Explicitly specify which fields should be shown | ||
| - Only listed fields will be visible | ||
| - Overrides any existing field configuration | ||
| - Order of fields is preserved as specified | ||
|
|
||
| 2. Using ``exclude``: Specify which fields should be hidden | ||
| - All fields except the listed ones will be visible | ||
| - Adds to any existing exclusions | ||
| - Original field order is maintained | ||
|
|
||
| Field customization applies to: | ||
| - Django admin views | ||
| - Admin forms | ||
| - Model admin fieldsets | ||
|
|
||
| .. note:: | ||
| Custom forms must be modified manually as they are not affected by this configuration. | ||
|
|
||
| Text Content | ||
| ----------- | ||
|
|
||
| The ``text.toml`` file allows customization of application text content: | ||
|
|
||
| .. code-block:: toml | ||
|
|
||
| [emails] | ||
| welcome_subject = "Welcome to {section_name}" | ||
| welcome_body = """ | ||
| Dear {name}, | ||
| Welcome to our organization... | ||
| """ | ||
|
|
||
| [messages] | ||
| success_registration = "Registration successful!" | ||
|
|
||
| Templates | ||
| --------- | ||
|
|
||
| Template Customization | ||
| ~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| You can override any template by placing a custom version in your project's templates directory: | ||
|
|
||
| 1. Create a directory structure matching the original template path | ||
| 2. Place your custom template file with the same name | ||
| 3. Django will use your custom template instead of the default | ||
|
|
||
| Example directory structure:: | ||
|
|
||
| templates/ | ||
| └── members/ | ||
| └── registration_form.tex | ||
| └── startpage/ | ||
| └── contact.html | ||
| └── impressum_content.html | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |||||||||||||
| from django.urls import path, reverse | ||||||||||||||
| from django.db import models | ||||||||||||||
| from django.contrib.admin import helpers, widgets | ||||||||||||||
| from django.conf import settings | ||||||||||||||
| import rules.contrib.admin | ||||||||||||||
| from rules.permissions import perm_exists | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -107,7 +108,74 @@ def get_queryset(self, request): | |||||||||||||
|
|
||||||||||||||
| #class ObjectPermissionsInlineModelAdminMixin(rules.contrib.admin.ObjectPermissionsInlineModelAdminMixin): | ||||||||||||||
|
|
||||||||||||||
| class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, FilteredQuerysetAdminMixin): | ||||||||||||||
| class FieldCustomizationMixin: | ||||||||||||||
| @property | ||||||||||||||
| def field_key(self): | ||||||||||||||
| """returns the key to look if model has custom fields in settings""" | ||||||||||||||
| return f"{self.model._meta.app_label}_{self.model.__name__}".lower() | ||||||||||||||
|
|
||||||||||||||
| def get_excluded_fields(self): | ||||||||||||||
| """if model has custom excluded fields in settings, return them as list""" | ||||||||||||||
| return settings.CUSTOM_MODEL_FIELDS.get(self.field_key, {}).get("exclude", []) | ||||||||||||||
|
|
||||||||||||||
| def get_included_fields(self): | ||||||||||||||
| """if model has an entire fieldset in settings, return them as list""" | ||||||||||||||
| return settings.CUSTOM_MODEL_FIELDS.get(self.field_key, {}).get("fields", []) | ||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Required for the change below. The same should be done for the |
||||||||||||||
|
|
||||||||||||||
| def get_fieldsets(self, request, obj=None): | ||||||||||||||
| """filter fieldsets according to included and excluded fields in settings""" | ||||||||||||||
|
|
||||||||||||||
| # get original fields and user-defined included and excluded fields | ||||||||||||||
| original_fieldsets = super().get_fieldsets(request, obj) | ||||||||||||||
| included = self.get_included_fields() | ||||||||||||||
| excluded = self.get_excluded_fields() | ||||||||||||||
|
|
||||||||||||||
| new_fieldsets = [] | ||||||||||||||
|
|
||||||||||||||
| for title, attrs in original_fieldsets: | ||||||||||||||
| fields = attrs.get("fields", []) | ||||||||||||||
|
|
||||||||||||||
| # custom fields take precedence over exclude | ||||||||||||||
| filtered_fields = [ | ||||||||||||||
| f | ||||||||||||||
| for f in fields | ||||||||||||||
| if ((not included or f in included) and (included or f not in excluded)) | ||||||||||||||
| ] | ||||||||||||||
|
|
||||||||||||||
| if filtered_fields: | ||||||||||||||
| # only add fieldset if it has any fields left | ||||||||||||||
| new_fieldsets.append( | ||||||||||||||
| (title, dict(attrs, **{"fields": filtered_fields})) | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| return new_fieldsets | ||||||||||||||
|
|
||||||||||||||
| def get_fields(self, request, obj=None): | ||||||||||||||
| """filter fields according to included and excluded fields in settings""" | ||||||||||||||
| fields = super().get_fields(request, obj) or [] | ||||||||||||||
| excluded = super().get_exclude(request, obj) or [] | ||||||||||||||
| custom_included = self.get_included_fields() | ||||||||||||||
| custom_excluded = self.get_excluded_fields() | ||||||||||||||
|
|
||||||||||||||
| if custom_included: | ||||||||||||||
| # custom included fields take precedence over exclude | ||||||||||||||
| return custom_included | ||||||||||||||
|
Comment on lines
+160
to
+162
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Otherwise, setting |
||||||||||||||
| return [f for f in fields if f not in custom_excluded and f not in excluded] | ||||||||||||||
|
|
||||||||||||||
| def get_exclude(self, request, obj=None): | ||||||||||||||
| """filter excluded fields according to included and excluded fields in settings""" | ||||||||||||||
| excluded = super().get_exclude(request, obj) or [] | ||||||||||||||
| custom_included = self.get_included_fields() | ||||||||||||||
| custom_excluded = self.get_excluded_fields() | ||||||||||||||
|
|
||||||||||||||
| if custom_included: | ||||||||||||||
| # custom included fields take precedence over exclude | ||||||||||||||
| return list((set(excluded) | set(custom_excluded)) - set(custom_included)) | ||||||||||||||
| return list(set(excluded) | set(custom_excluded)) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, FilteredQuerysetAdminMixin, FieldCustomizationMixin): | ||||||||||||||
| def has_add_permission(self, request, obj=None): | ||||||||||||||
| assert obj is None | ||||||||||||||
| opts = self.opts | ||||||||||||||
|
|
@@ -194,7 +262,7 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): | |||||||||||||
|
|
||||||||||||||
| # For any other type of field, just call its formfield() method. | ||||||||||||||
| return db_field.formfield(**kwargs) | ||||||||||||||
|
|
||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| class CommonAdminInlineMixin(CommonAdminMixin): | ||||||||||||||
| def has_add_permission(self, request, obj): | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add this guide to the table of contents for the developer manual?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW: the documentation got deployed at https://chrisflav.github.io/kompass/2/merge/development_manual/customization. Unfortunately, I have no idea why it is deployed at this strange url
2/mergeinstead ofMK/conditional_fields.