django / django/new-features

Ability to customise model Meta & migration generation

Open
#73 14 comments 25 reactions 0 assignees View on GitHub
Dominant language
No language data
Stars
188
Forks
7
PR merge metrics
No merged PRs in 30d

Description

### Code of Conduct

- [x] I agree to follow Django's Code of Conduct

### Feature Description

Allow the ability to build supporting functionality around a model by customising Meta & migration generation without monkey patching.

### Problem

### What does "build functionality around a model" mean?

Basically extending the model in ways that the Django framework does not yet support but may be supported by the underlying database (…or even just other non-database functionality not yet conceived)

Some common use cases I've come across:

- Initialise with prerequisite data
- Triggers
- Row Level Security
- Temporal tables
- Database views
- Scheduled tasks (eg with pg_cron)
- Adding custom functions, types & other tidbits related to the model

### The current ways to build functionality around a model

**1. Custom migrations:** manually written and added to `migrations/`. This can be tedious and doesn't lend itself to 3rd party libraries well. Code colocality is quite low. A lot of Django developers I come across often admit that they will forget about custom migrations added, or don't realise their presence when being brought onto a project. Newer developers often don't even know that custom migrations are a thing.

I've seen some folks do things like this to help with the discoverability:
```python
class Foo(Model):
...

class Meta:
...
# The following custom migrations for this model have been added:
# 1. Additional composite foreign key to enforce tenancy (see ....)
# 2. ... etc ...
```

**2. Abuse `Meta.constraints`:** Constraints aren't really designed for a general purpose model customisation **but** it seems to work well enough that I often use this hack to do this. See: https://github.com/shangxiao/stupid-django-tricks/tree/master/abusing_constraints

**3. Monkey Patching:** Take a look at [django-pgtrigger](https://github.com/AmbitionEng/django-pgtrigger) works to see what patching needs to happen.

### Request or proposal

proposal

### Additional Details

### Some examples of how this would be beneficial

#### Initial data

```python
class ValidDomain(Model):
domain = CharField()

class Meta:
initial_data = [
# sad face that we can't refer to ValidDomain within itself
Self(domain="foo.com.hk"),
Self(domain="bar.com"),
]
```

#### System-versioned tables with MariaDB

```python
class Company(Model):
name = CharField(...)
...

class Meta:
system_versioned = True
```

#### Views

```python
class Account(Model):
name = CharField()
is_active = BooleanField()

class Meta:
views = [
View(name="ActiveAccounts", condition=Q(is_active=True), materialized=False),
]

# alternatively
class ActiveAccount(Model):
name = CharField()

class Meta:
db_view = True
query = Account.objects.filter(is_active=True)
materialized=False
```

### Implementation Suggestions

Some notes here on what may need to change, garnered from django-pgtrigger:

This would require:

1. allowing `django.db.models.options.DEFAULT_NAMES` to be extendable
2. allowing `django.db.models.migrations.state.DEFAULT_NAMES` to be extendable
3. allowing `MigrationAutodetector` to be extendable. Just looking over the `_detect_changes()` it all looks very order specific and no obvious place to hook into but that might just be up to the user to determine the most appropriate place, a special hook may not be necessary.

We can already do:

1. Define custom migration operations
2. Extend DB backends to cater for backend specific syntax, like outright table creation for eg

**Edit** I missed this but [ticket #35656](https://code.djangoproject.com/ticket/35656) allowed specifying the autodetector class as a class attribute on the `makemigrations` & `migrate` commands in order to allow customisation in subclassed commands.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.