一键导入
django-new-model
Create a new Django model following modern best practices
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create a new Django model following modern best practices
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Configure the current project with Django 6.x expert tools (rules, skills, agents, hooks) from GitHub. Use when setting up a Django project.
Debug Django issues - ORM queries, migrations, template errors, async problems. Use when debugging Django applications.
Check and validate Django migrations for safety and correctness before deployment
Create a new REST API endpoint following modern DRF best practices
Create a new Django app following modern best practices and cookiecutter-django conventions
Analyze and optimize Django application performance
| name | django-new-model |
| description | Create a new Django model following modern best practices |
| disable-model-invocation | true |
| argument-hint | [ModelName] |
| allowed-tools | Read, Write, Edit, Bash, Grep, Glob |
Follow these steps to create a production-quality Django model.
models.py: add the model there.models/ package: create models/<model_name>.py and import in models/__init__.py.from django.conf import settings
from django.db import models
from django.db.models import Q
from django.db.models.functions import Now
from django.urls import reverse
from core.models import TimeStampedModel # or django_extensions.db.models
class <Model>Manager(models.Manager):
"""Custom manager for <Model>."""
def active(self):
return self.filter(is_active=True)
def for_user(self, user):
return self.filter(user=user)
class <Model>(TimeStampedModel):
"""
<One-line description of what this model represents.>
Attributes:
user: The owner of this <model>.
title: Human-readable title.
status: Current lifecycle status.
"""
class Status(models.TextChoices):
DRAFT = "draft", "Draft"
ACTIVE = "active", "Active"
ARCHIVED = "archived", "Archived"
# === Relationships ===
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="<model_plural>",
)
# === Fields ===
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
description = models.TextField(blank=True, default="")
status = models.CharField(
max_length=20,
choices=Status,
db_default=Status.DRAFT,
)
is_active = models.BooleanField(db_default=True)
amount = models.DecimalField(
max_digits=10,
decimal_places=2,
db_default=0,
)
# === Database-computed fields (Django 5.0+) ===
# full_name = models.GeneratedField(
# expression=Concat("first_name", Value(" "), "last_name"),
# output_field=models.CharField(max_length=255),
# db_persist=True,
# )
# === Timestamps (inherited from TimeStampedModel) ===
# created, modified
# === Managers ===
objects = <Model>Manager()
class Meta:
ordering = ["-created"]
verbose_name = "<model>"
verbose_name_plural = "<model_plural>"
indexes = [
models.Index(fields=["status", "-created"]),
models.Index(fields=["user", "status"]),
models.Index(fields=["slug"]),
]
constraints = [
models.CheckConstraint(
check=Q(amount__gte=0),
name="%(app_label)s_%(class)s_amount_non_negative",
),
models.UniqueConstraint(
fields=["user", "slug"],
name="%(app_label)s_%(class)s_unique_user_slug",
),
]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("<app_name>:detail", kwargs={"pk": self.pk})
# === Properties ===
@property
def is_draft(self):
return self.status == self.Status.DRAFT
@property
def is_editable(self):
return self.status in {self.Status.DRAFT, self.Status.ACTIVE}
| Data | Field | Notes |
|---|---|---|
| Short text | CharField(max_length=N) | Always set max_length |
| Long text | TextField() | blank=True, default="" if optional |
| Integer | IntegerField() or PositiveIntegerField() | Use Positive for counts |
| Decimal (money) | DecimalField(max_digits=M, decimal_places=N) | Never use Float for money |
| Boolean | BooleanField(db_default=True) | Use db_default |
| Date | DateField() | |
| DateTime | DateTimeField() | Use db_default=Now() |
EmailField() | Has built-in validation | |
| URL | URLField() | Has built-in validation |
| UUID | UUIDField(default=uuid.uuid4) | Good for public-facing IDs |
| File | FileField(upload_to="path/") | Always set upload_to |
| Image | ImageField(upload_to="path/") | Requires Pillow |
| JSON | JSONField(default=dict) | Use default=dict or default=list |
| IP | GenericIPAddressField() | |
| Slug | SlugField(max_length=255) | |
| Enum | CharField(choices=TextChoices) | Use TextChoices |
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, # or PROTECT, SET_NULL, SET_DEFAULT
related_name="orders",
)
tags = models.ManyToManyField("tags.Tag", related_name="articles", blank=True)
class OrderItem(TimeStampedModel):
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="items")
product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="order_items")
quantity = models.PositiveIntegerField(db_default=1)
unit_price = models.DecimalField(max_digits=10, decimal_places=2)
profile = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="profile",
)
@admin.register(<Model>)
class <Model>Admin(admin.ModelAdmin):
list_display = ["__str__", "user", "status", "created"]
list_filter = ["status", "created"]
search_fields = ["title", "user__email"]
readonly_fields = ["created", "modified"]
prepopulated_fields = {"slug": ("title",)}
raw_id_fields = ["user"]
ordering = ["-created"]
python manage.py makemigrations <app_name>
# REVIEW the migration file before proceeding
python manage.py migrate
class <Model>Factory(DjangoModelFactory):
class Meta:
model = <Model>
user = factory.SubFactory(UserFactory)
title = factory.Faker("sentence", nb_words=4)
slug = factory.LazyAttribute(lambda o: slugify(o.title))
status = <Model>.Status.DRAFT
amount = factory.Faker("pydecimal", left_digits=4, right_digits=2, positive=True)
@pytest.mark.django_db
class Test<Model>:
def test_str(self):
obj = <Model>Factory(title="Test")
assert str(obj) == "Test"
def test_get_absolute_url(self):
obj = <Model>Factory()
assert obj.get_absolute_url() == f"/<app>/{obj.pk}/"
def test_amount_non_negative_constraint(self):
with pytest.raises(IntegrityError):
<Model>Factory(amount=-1)
def test_unique_user_slug_constraint(self):
obj = <Model>Factory(slug="test-slug")
with pytest.raises(IntegrityError):
<Model>Factory(user=obj.user, slug="test-slug")
def test_default_status_is_draft(self):
obj = <Model>Factory()
assert obj.status == <Model>.Status.DRAFT
def test_manager_active(self):
active = <Model>Factory(is_active=True)
<Model>Factory(is_active=False)
assert list(<Model>.objects.active()) == [active]
TimeStampedModelTextChoices/IntegerChoices used for enum fieldsnull=True on CharField/TextFielddb_default used where appropriateMeta class with ordering, indexes, constraints__str__ definedget_absolute_url defined (if applicable)related_name on all FK/M2M fields