跳到正文
cccskills
免费在 GitHub 公开

django-uv

Provides expert knowledge for building Django 5+ projects with UV as the package manager, domain-driven app structure, ruff for linting, and pytest-django for testing.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md9.4 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Django + UV

Project Setup with UV

UV replaces pip, pip-tools, virtualenv, and pyenv in a single binary. Always use UV for dependency management.

# Create project
uv init my-project
cd my-project

# Add Django and dependencies
uv add django psycopg[binary] djangorestframework django-environ

# Add dev dependencies
uv add --dev pytest pytest-django ruff factory-boy coverage

# Run management commands
uv run python manage.py migrate
uv run python manage.py runserver

pyproject.toml Layout

[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "django>=5.0",
    "psycopg[binary]>=3.1",
    "djangorestframework>=3.15",
    "django-environ>=0.11",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-django>=4.8",
    "ruff>=0.4",
    "factory-boy>=3.3",
    "coverage>=7.4",
]

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "SIM"]
ignore = ["E501"]

[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.test"
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

[tool.coverage.run]
source = ["."]
omit = ["*/migrations/*", "*/tests/*", "manage.py"]

App Structure (Domain-Driven)

Organize by domain, not by technical layer. Each Django app owns its domain entirely.

my_project/
  config/
    settings/
      base.py       — shared settings
      dev.py        — local overrides
      test.py       — test-specific settings
      prod.py       — production settings
    urls.py
    wsgi.py
  apps/
    users/
      models.py
      services.py   — business logic, never in views
      views.py      — thin; delegates to services
      serializers.py
      admin.py
      urls.py
      tests/
        test_models.py
        test_services.py
        test_views.py
        factories.py
    orders/
      ...
  manage.py
  pyproject.toml

Settings Split

# config/settings/base.py
from pathlib import Path
import environ

env = environ.Env()
BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG", default=False)

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "rest_framework",
    "apps.users",
    "apps.orders",
]

DATABASES = {
    "default": env.db("DATABASE_URL")
}
# config/settings/test.py
from .base import *  # noqa: F403

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": ":memory:",
    }
}

# Never send real emails in tests
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"

Models

# apps/users/models.py
import uuid
from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField(unique=True)
    bio = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

    class Meta:
        ordering = ["-created_at"]

    def __str__(self) -> str:
        return self.email

Rules:

  • Use UUID primary keys — never expose sequential integer IDs in public APIs
  • auto_now_add and auto_now for timestamps — never set manually
  • Always define __str__ — it appears in admin and error messages
  • Use blank=True for optional strings (form validation), null=True only for nullable FK/non-string fields

Service Layer

Business logic lives in services.py. Views are thin HTTP adapters.

# apps/users/services.py
from django.db import transaction
from django.contrib.auth.hashers import make_password
from .models import User


class UserService:

    @staticmethod
    def create_user(*, email: str, password: str, username: str) -> User:
        if User.objects.filter(email=email).exists():
            raise ValueError(f"Email {email!r} is already registered")

        with transaction.atomic():
            user = User.objects.create(
                email=email,
                username=username,
                password=make_password(password),
            )
        return user

    @staticmethod
    def deactivate_user(*, user_id: str) -> User:
        user = User.objects.get(id=user_id)
        user.is_active = False
        user.save(update_fields=["is_active", "updated_at"])
        return user

Rules:

  • Use keyword-only arguments (* separator) in service methods — prevents positional argument confusion
  • Use update_fields in save() — avoids overwriting unintended fields and reduces the UPDATE query scope
  • Wrap multi-step writes in transaction.atomic()
  • Never import from views.py in services.py — keep the dependency direction strict: views → services → models

Views (DRF)

# apps/users/views.py
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response

from .serializers import CreateUserSerializer, UserSerializer
from .services import UserService


@api_view(["POST"])
def create_user(request: Request) -> Response:
    serializer = CreateUserSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)

    user = UserService.create_user(**serializer.validated_data)
    return Response(UserSerializer(user).data, status=status.HTTP_201_CREATED)


@api_view(["DELETE"])
@permission_classes([IsAuthenticated])
def deactivate_user(request: Request, user_id: str) -> Response:
    UserService.deactivate_user(user_id=user_id)
    return Response(status=status.HTTP_204_NO_CONTENT)

Testing with pytest-django

Test Factories

# apps/users/tests/factories.py
import factory
from factory.django import DjangoModelFactory
from apps.users.models import User


class UserFactory(DjangoModelFactory):
    class Meta:
        model = User

    email = factory.Sequence(lambda n: f"user{n}@example.com")
    username = factory.Sequence(lambda n: f"user{n}")
    password = factory.PostGenerationMethodCall("set_password", "testpass123")
    is_active = True

Service Tests

# apps/users/tests/test_services.py
import pytest
from apps.users.services import UserService


@pytest.mark.django_db
class TestUserService:

    def test_create_user_succeeds_with_valid_data(self):
        user = UserService.create_user(
            email="new@example.com",
            password="secure123",
            username="newuser",
        )

        assert user.pk is not None
        assert user.email == "new@example.com"
        assert user.check_password("secure123")

    def test_create_user_raises_when_email_exists(self, user_factory):
        existing = user_factory(email="taken@example.com")

        with pytest.raises(ValueError, match="already registered"):
            UserService.create_user(
                email=existing.email,
                password="any",
                username="other",
            )

View Tests

# apps/users/tests/test_views.py
import pytest
from django.urls import reverse


@pytest.mark.django_db
class TestCreateUser:

    def test_returns_201_with_valid_payload(self, client):
        response = client.post(
            reverse("users:create"),
            data={"email": "new@example.com", "password": "secure123", "username": "new"},
            content_type="application/json",
        )

        assert response.status_code == 201
        assert response.data["email"] == "new@example.com"

    def test_returns_400_with_invalid_email(self, client):
        response = client.post(
            reverse("users:create"),
            data={"email": "not-an-email", "password": "x", "username": "y"},
            content_type="application/json",
        )

        assert response.status_code == 400

    def test_returns_400_when_email_already_exists(self, client, user_factory):
        user_factory(email="taken@example.com")

        response = client.post(
            reverse("users:create"),
            data={"email": "taken@example.com", "password": "pass", "username": "z"},
            content_type="application/json",
        )

        assert response.status_code == 400

conftest.py

# conftest.py
import pytest
from pytest_factoryboy import register
from apps.users.tests.factories import UserFactory

register(UserFactory)   # makes user_factory fixture available everywhere

Linting

# Check
uv run ruff check .

# Fix auto-fixable issues
uv run ruff check --fix .

# Format
uv run ruff format .

Run ruff in CI on every pull request. Do not merge code that fails lint.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when adding monitoring, metrics, logging, or tracing to Spring AI or LLM integration code. Covers token tracking, latency measurement, cost estimation, and prompt/response logging. Use when user mentions AI monitoring, token costs, or LLM observability.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月20日 更新

android

免费

Provides expert knowledge for building modern native Android apps using Kotlin, Jetpack Compose, Material Design 3, MVI architecture, Hilt dependency injection, and Media3/ExoPlayer.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月20日 更新

Design, implement, review, and audit Android Jetpack Compose UI/UX for phone interfaces. Use when a request concerns concrete Compose layout, components, interaction, accessibility, or visual behavior. Do not activate for framework or dependency presence alone; non-UI Android work is excluded.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月20日 更新

Provides expert knowledge for designing, implementing, and managing REST API versioning strategies with deprecation workflows.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月20日 更新

astro

免费

Provides expert knowledge for building Astro 5+ sites with Islands Architecture, Content Collections, TypeScript, and performance-first rendering strategies.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月20日 更新

Audits token storage mechanisms to prevent XSS-based JWT theft.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月20日 更新

查看 lgzarturo 的全部技能

报告此技能的问题