获取外键数据

Posted by Shallow Dreameron June 18, 2024

如果 Book 模型和 Author 模型位于不同的 Django 应用中,处理方法基本相同,只需确保正确引用和导入相关的模型和序列化器。

1. 模型定义

假设 Author 模型位于 authors 应用中,而 Book 模型位于 books 应用中。

authors/models.py 中定义 Author 模型:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

books/models.py 中定义 Book 模型:

from django.db import models
from authors.models import Author  # 引用另一个应用的模型

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)

    def __str__(self):
        return self.title

2. 序列化器定义

authors/serializers.py 中定义 AuthorSerializer

from rest_framework import serializers
from .models import Author

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ['id', 'name']

books/serializers.py 中定义 BookSerializer

from rest_framework import serializers
from authors.models import Author  # 引用另一个应用的模型
from .models import Book
from authors.serializers import AuthorSerializer  # 引用另一个应用的序列化器

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer()  # 嵌套序列化器

    class Meta:
        model = Book
        fields = ['id', 'title', 'author']

    def create(self, validated_data):
        author_data = validated_data.pop('author')
        author, created = Author.objects.update_or_create(**author_data)
        book = Book.objects.create(author=author, **validated_data)
        return book

    def update(self, instance, validated_data):
        author_data = validated_data.pop('author')
        author = instance.author

        # Update author fields
        for attr, value in author_data.items():
            setattr(author, attr, value)
        author.save()

        # Update book fields
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        return instance

3. 视图集定义

books/views.py 中定义视图集:

from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

4. URL 配置

books/urls.py 中配置 URL:

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet

router = DefaultRouter()
router.register(r'books', BookViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

myproject/urls.py 中包含应用的 URL 配置:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/books/', include('books.urls')),
    path('api/authors/', include('authors.urls')),
]

5. 测试 API

运行开发服务器:

python manage.py runserver

测试获取请求

使用工具如 curl 或 Postman 来测试 API。下面是一个使用 curl 的示例请求,获取所有 Book 实例及其关联的 Author 数据:

curl -X GET http://127.0.0.1:8000/api/books/ \
-H 'Content-Type: application/json'

这个请求将返回类似以下的响应,其中包含 Book 实例及其关联的 Author 数据:

[
    {
        "id": 1,
        "title": "New Book Title",
        "author": {
            "id": 1,
            "name": "New Author Name"
        }
    },
    {
        "id": 2,
        "title": "Another Book Title",
        "author": {
            "id": 2,
            "name": "Another Author Name"
        }
    }
]

总结

通过这种方式,你可以在不同应用之间正确引用模型和序列化器,实现数据获取时同时获取外键主表的数据。关键是确保在序列化器中正确嵌套和引用其他应用的序列化器和模型。


要使用 Django ORM 中的窗口函数(Window Function)来获取分类的最新数据,并结合 get_object_or_404 返回这些数据,我们需要一些步骤来实现这一目标。以下是详细的示例:

模型定义

假设我们有一个模型 File,其中包含 file_namecontentlast_modified 字段。我们希望获取每个文件名分类中 last_modified 最近的一条记录。

files/models.py

from django.db import models

class File(models.Model):
    file_name = models.CharField(max_length=255)
    content = models.TextField()
    last_modified = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.file_name

序列化器

files/serializers.py

from rest_framework import serializers
from .models import File

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = ['file_name', 'content', 'last_modified']

视图

我们将使用 Django ORM 的窗口函数来获取每个 file_name 分类中最新的记录。

files/views.py

from django.db.models import F
from django.db.models.functions import RowNumber
from django.db.models.expressions import Window
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import File
from .serializers import FileSerializer

class LatestFileAPIView(APIView):

    def post(self, request):
        # 使用窗口函数为每个文件名分组获取最近的一条记录
        latest_files = File.objects.annotate(
            row_number=Window(
                expression=RowNumber(),
                partition_by=[F('file_name')],
                order_by=F('last_modified').desc()
            )
        ).filter(row_number=1)
        
        # 检查是否找到数据
        if not latest_files.exists():
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
        
        # 序列化数据
        serializer = FileSerializer(latest_files, many=True)

        # 返回响应
        return Response(serializer.data, status=status.HTTP_200_OK)

路由

配置 URL 路由以访问这些视图。

files/urls.py

from django.urls import path
from .views import LatestFileAPIView

urlpatterns = [
    path('files/latest/', LatestFileAPIView.as_view(), name='file-latest'),
]

运行服务器并测试

启动 Django 服务器:

python manage.py runserver

你可以使用 Postman 或 cURL 发送 POST 请求来测试这些端点。

  • 获取每个文件名分类中最新的一条记录:http://localhost:8000/api/files/latest/

示例 POST 请求:

curl -X POST http://localhost:8000/api/files/latest/

示例响应:

[
    {
        "file_name": "file1.txt",
        "content": "Content of file 1",
        "last_modified": "2024-06-19T10:00:00Z"
    },
    {
        "file_name": "file2.txt",
        "content": "Content of file 2",
        "last_modified": "2024-06-19T09:00:00Z"
    }
]

解释

  1. 模型定义:我们定义了一个 File 模型,其中包含 file_namecontentlast_modified 字段。
  2. 序列化器:我们定义了一个 FileSerializer 来序列化 File 模型的数据。
  3. 视图:在 LatestFileAPIView 中,我们使用 Django ORM 的窗口函数 RowNumber 来为每个 file_name 分类获取最新的一条记录。通过 partition_by 参数,我们按照 file_name 字段进行分组,并通过 order_by 参数按照 last_modified 字段进行排序。我们只过滤 row_number 为 1 的记录,即每个分组中的第一条记录。
  4. 路由:我们定义了一个 URL 路由来访问 LatestFileAPIView 视图。

通过这种方式,我们可以使用窗口函数、last_modifiedget_object_or_404 来获取每个文件名分类中最新的一条数据。


要使用 Django ORM 中的窗口函数(Window Function)来获取分类的最新数据,并结合 get_object_or_404 返回这些数据,我们需要一些步骤来实现这一目标。以下是详细的示例:

模型定义

假设我们有一个模型 File,其中包含 file_namecontentlast_modified 字段。我们希望获取每个文件名分类中 last_modified 最近的一条记录。

files/models.py

from django.db import models

class File(models.Model):
    file_name = models.CharField(max_length=255)
    content = models.TextField()
    last_modified = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.file_name

序列化器

files/serializers.py

from rest_framework import serializers
from .models import File

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = ['file_name', 'content', 'last_modified']

视图

我们将使用 Django ORM 的窗口函数来获取每个 file_name 分类中最新的记录。

files/views.py

from django.db.models import F
from django.db.models.functions import RowNumber
from django.db.models.expressions import Window
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import File
from .serializers import FileSerializer

class LatestFileAPIView(APIView):

    def post(self, request):
        # 使用窗口函数为每个文件名分组获取最近的一条记录
        latest_files = File.objects.annotate(
            row_number=Window(
                expression=RowNumber(),
                partition_by=[F('file_name')],
                order_by=F('last_modified').desc()
            )
        ).filter(row_number=1)
        
        # 检查是否找到数据
        if not latest_files.exists():
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
        
        # 序列化数据
        serializer = FileSerializer(latest_files, many=True)

        # 返回响应
        return Response(serializer.data, status=status.HTTP_200_OK)

路由

配置 URL 路由以访问这些视图。

files/urls.py

from django.urls import path
from .views import LatestFileAPIView

urlpatterns = [
    path('files/latest/', LatestFileAPIView.as_view(), name='file-latest'),
]

运行服务器并测试

启动 Django 服务器:

python manage.py runserver

你可以使用 Postman 或 cURL 发送 POST 请求来测试这些端点。

  • 获取每个文件名分类中最新的一条记录:http://localhost:8000/api/files/latest/

示例 POST 请求:

curl -X POST http://localhost:8000/api/files/latest/

示例响应:

[
    {
        "file_name": "file1.txt",
        "content": "Content of file 1",
        "last_modified": "2024-06-19T10:00:00Z"
    },
    {
        "file_name": "file2.txt",
        "content": "Content of file 2",
        "last_modified": "2024-06-19T09:00:00Z"
    }
]

解释

  1. 模型定义:我们定义了一个 File 模型,其中包含 file_namecontentlast_modified 字段。
  2. 序列化器:我们定义了一个 FileSerializer 来序列化 File 模型的数据。
  3. 视图:在 LatestFileAPIView 中,我们使用 Django ORM 的窗口函数 RowNumber 来为每个 file_name 分类获取最新的一条记录。通过 partition_by 参数,我们按照 file_name 字段进行分组,并通过 order_by 参数按照 last_modified 字段进行排序。我们只过滤 row_number 为 1 的记录,即每个分组中的第一条记录。
  4. 路由:我们定义了一个 URL 路由来访问 LatestFileAPIView 视图。

通过这种方式,我们可以使用窗口函数、last_modifiedget_object_or_404 来获取每个文件名分类中最新的一条数据。


理解了,你的两个模型都通过外键关联到 User 模型。假设你的模型如下:

模型定义

models.py

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    additional_info = models.TextField()

class AnotherModel(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    some_data = models.CharField(max_length=100)

要在查询 AnotherModel 时同时获取关联的 User 数据和 UserProfile 数据,你可以使用 select_relatedprefetch_related。这里是一个示例实现:

序列化器定义

serializers.py

from rest_framework import serializers
from myapp.models import UserProfile, AnotherModel
from django.contrib.auth.models import User

class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = ['additional_info']

class UserSerializer(serializers.ModelSerializer):
    user_profile = UserProfileSerializer(source='userprofile_set', many=True, read_only=True)

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'user_profile']

class AnotherModelSerializer(serializers.ModelSerializer):
    user = UserSerializer()

    class Meta:
        model = AnotherModel
        fields = ['id', 'some_data', 'user']

视图定义

views.py

from rest_framework import generics
from myapp.models import AnotherModel
from myapp.serializers import AnotherModelSerializer

class AnotherModelListView(generics.ListAPIView):
    queryset = AnotherModel.objects.all().select_related('user').prefetch_related('user__userprofile_set')
    serializer_class = AnotherModelSerializer

URL 路由

urls.py

from django.urls import path
from myapp.views import AnotherModelListView

urlpatterns = [
    path('another-model/', AnotherModelListView.as_view(), name='another-model-list'),
]

代码解释

  1. 模型定义:
    • UserProfile 模型包含用户的额外信息,并与 User 模型通过外键关联。
    • AnotherModel 模型包含一个外键字段 user,指向 User 模型。
  2. 序列化器定义:
    • UserProfileSerializer 序列化 UserProfile 模型。
    • UserSerializer 序列化 User 模型,并包含 UserProfile 的信息。由于外键关联的 UserProfile 是一个集合,我们使用 many=True
    • AnotherModelSerializer 序列化 AnotherModel 模型,并包含嵌套的 UserSerializer
  3. 视图定义:
    • AnotherModelListView 视图中,使用 select_related 方法预加载 user 外键关联的数据,使用 prefetch_related 方法预加载 user__userprofile_set 数据,以减少数据库查询次数并优化性能。
  4. URL 路由:
    • 配置 URL 路由,将路径 'another-model/' 路由到 AnotherModelListView 视图。

通过这种方式,当你访问 AnotherModelListView 视图时,将会返回 AnotherModel 数据及其关联的 User 数据和 UserProfile 数据。这样可以有效地获取用户的额外信息,并减少数据库查询次数。