我对Django 很陌生.我正在开发一个需要存储电影的Django应用程序,当我try 使用以下程序加载电影实例时:

import os
import json
import requests

# Assuming the JSON content is stored in a variable named 'films_json'
json_file_path: str = os.path.join(os.getcwd(), "films.json")

# Load the JSON content from the file
with open(json_file_path, "r") as file:
    films_json: dict = json.load(file)

# URL of the endpoint
endpoint_url = "http://127.0.0.1:8000/site-admin/add-film/"

for film_data in films_json["films"]:
    print()
    print(json.dumps(film_data, indent=4))
    response: requests.Response = requests.post(endpoint_url, json=film_data)
    if response.status_code == 201:
        print(f"Film '{film_data['name']}' added successfully")
    else:
        print(f"Failed to add film '{film_data['name']}':")
        print(json.dumps(dict(response.headers),indent=4))
        print(f"{json.dumps(response.json(), indent=4)}")

我得到以下回复(对于每部电影):

{
    "name": "Pulp Fiction",
    "genre": "Crime",
    "duration": 154,
    "director": "Quentin Tarantino",
    "cast": [
        "John Travolta",
        "Uma Thurman",
        "Samuel L. Jackson",
        "Bruce Willis"
    ],
    "description": "The lives of two mob hitmen, a boxer, a gangster's wife, and a pair of diner bandits intertwine in four tales of violence and redemption.",
    "image_url": "https://m.media-amazon.com/images/M/MV5BNGNhMDIzZTUtNTBlZi00MTRlLWFjM2ItYzViMjE3YzI5MjljXkEyXkFqcGdeQXVyNzkwMjQ5NzM@._V1_.jpg",
    "release": "1994-00-00"
}
Failed to add film 'Pulp Fiction':
Bad Request
{
    "Date": "Fri, 03 May 2024 22:35:32 GMT",
    "Server": "WSGIServer/0.2 CPython/3.10.10",
    "Content-Type": "application/json",
    "Vary": "Accept, Cookie",
    "Allow": "POST, OPTIONS",
    "djdt-store-id": "bf9dc157db354419a70d69f3be5e8dd7",
    "Server-Timing": "TimerPanel_utime;dur=0;desc=\"User CPU time\", TimerPanel_stime;dur=0;desc=\"System CPU time\", TimerPanel_total;dur=0;desc=\"Total CPU time\", TimerPanel_total_time;dur=50.99039999186061;desc=\"Elapsed time\", SQLPanel_sql_time;dur=2.606799971545115;desc=\"SQL 14 queries\", CachePanel_total_time;dur=0;desc=\"Cache 0 Calls\"",
    "X-Frame-Options": "DENY",
    "Content-Length": "197",
    "X-Content-Type-Options": "nosniff",
    "Referrer-Policy": "same-origin",
    "Cross-Origin-Opener-Policy": "same-origin"
}
{
    "release": [
        "Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
    ],
    "director_id": [
        "Invalid pk \"1\" - object does not exist."
    ],
    "cast": [
        "Invalid pk \"2\" - object does not exist."
    ]
}

这是我的观点:

class AggregateFilmView(generics.CreateAPIView):
    serializer_class: type = FilmSerializer

    def post(self, request) -> Response:

        print(f"###     {type(request.data)}", file=sys.stderr)
        with transaction.atomic():  # Ensure all or nothing persistence

            try:
                film_data: dict = request.data

                # Handle Director
                if "director" not in film_data.keys():
                    raise ValidationError("director field must be provided")
                director_name: str = film_data.pop("director", {})
                if not type(director_name) is str:
                    raise ValidationError("diretor name must be a string")
                director_data: dict[str, str] = {"name": director_name}
                director_serializer = DirectorSerializer(data=director_data)
                director_serializer.is_valid(raise_exception=True)
                director: Director = director_serializer.save()

                # Handle Cast (Actors)
                if "cast" not in film_data.keys():
                    raise ValidationError("cast field must be provided")
                cast_names: list[str] = film_data.pop("cast", [])
                if type(cast_names) is not list:
                    raise ValidationError("cast must be a list of names (list[string])")
                cast_data: list[dict[str, str]] = []
                if not len(cast_names) == 0:
                    for name in cast_names:
                        if not type(name) is str:
                            raise ValidationError(
                                "cast must be a list of names (list[string])"
                            )
                        actor_data: dict[str, str] = {"name": name}
                        cast_data.append(actor_data)
                    actor_serializer = ActorSerializer(data=cast_data, many=True)
                    actor_serializer.is_valid(raise_exception=True)
                    actors: Actor = actor_serializer.save()

                # Handle Film
                film_data["director_id"] = director.id  # director ID
                film_data["cast"] = [actor.id for actor in actors]  # list of actor IDs
                types: list[type] = [type(field) for field in film_data.values()]
                print(types, file=sys.stderr)
                print(film_data, file=sys.stderr)
                film_serializer = FilmSerializer(data=film_data)
                film_serializer.is_valid(raise_exception=True)
                film_serializer.save()

                response: Response = Response(
                    {"detail": "Film added succesfully"}, status=status.HTTP_201_CREATED
                )

            except ValidationError as error:
                response = Response(
                    {"error": error.message},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Return serialized data (customize as needed)
            return response

这是我的序列化器:

class FilmSerializer(serializers.ModelSerializer):

    class Meta:
        model = Film
        # fields: str = "__all__"
        fields: list[str] = [
            "name",
            "release",
            "genre",
            "description",
            "duration",
            "director_id",
            "cast",
        ]

    def validate_name(self, value) -> str:
        print(f"Name validation {value}", file=sys.stderr)
        if value is None:
            raise serializers.ValidationError("Film name is required")
        return value

    def validate_release(self, value) -> str:
        print(f"Date validation {value}", file=sys.stderr)
        if value is None:
            raise serializers.ValidationError("Release year is required")
        try:
            patterns: dict[str, str] = {
                "%Y": r"^\d{4}$",
                "%Y-%m": r"^\d{4}-\d{2}$",
                "%Y-%m-%d": r"^\d{4}-\d{2}-\d{2}$",
                "%m-%Y": r"^\d{2}-\d{4}$",
                "%d-%m-%Y": r"^\d{2}-\d{2}-\d{4}$",
            }
            format_match: bool = False
            for date_format, pattern in patterns.items():
                if re.match(pattern, value):
                    format_match = True
                    print(f"Matched {date_format} format", file=sys.stderr)
                    return datetime.datetime.strptime(value, date_format)

            if not format_match:
                raise serializers.ValidationError(
                    "Invalid date format.",
                    "Accepted formats: %Y, %Y-%m, %Y-%m-%d, %m-%Y, %d-%m-%Y",
                )

        except ValueError:
            raise serializers.ValidationError("Year must be a valid date")
        return value

    def validate_genre(self, value) -> str:
        print(f"Genre validation {value}", file=sys.stderr)
        if value is None:
            raise serializers.ValidationError("Genre is required")
        if value not in Film.GENRE_CHOICES:
            raise serializers.ValidationError("Invalid genre")
        return value

    def validate_description(self, value) -> str:
        print(f"Description validation {value}", file=sys.stderr)
        if value is None:
            raise serializers.ValidationError("Description is required")
        if len(value) > 300:
            raise serializers.ValidationError(
                "Description cannot exceed 300 characters"
            )
        return value

    def validate_director_id(self, value) -> str:
        print(f"Director Id {value}", file=sys.stderr)
        if value is None:
            raise serializers.ValidationError("Director is required")
        try:
            Director.objects.get(pk=value)
        except Director.DoesNotExist:
            raise serializers.ValidationError("Invalid director ID")
        return value

    def validate_cast(self, value) -> str:
        print(f"Cast {value}", file=sys.stderr)
        if value is None:
            raise serializers.ValidationError("At least one cast member is required")
        try:
            Actor.objects.filter(pk__in=value)
        except Actor.DoesNotExist:
            raise serializers.ValidationError("Invalid cast ID")

这是我的模型:

class Film(models.Model):

    GENRE_CHOICES: list[str] = [
        "Action",
        "Comedy",
        "Crime",
        "Doctumentary",
        "Drama",
        "Horror",
        "Romance",
        "Sci-Fi",
        "Thriller",
        "Western",
    ]

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=64, unique=True)
    release = models.DateField()
    genre = models.CharField(max_length=64)
    description = models.TextField()
    duration = models.FloatField()
    director_id = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="director"
    )
    cast = models.ManyToManyField(User, related_name="cast")

问题在于,release字段被与我在序列化器中定义的验证不同的验证拒绝,并且应该通过修改我的validate_release方法应该执行的值来避免它报告的无效.

问题是validate_release方法没有被调用,正如在运行服务器的终端中可以看到的那样:

###     <class 'dict'>
[<class 'str'>, <class 'str'>, <class 'int'>, <class 'str'>, <class 'str'>, <class 'str'>, <class 'int'>, <class 'list'>]
{'name': 'Pulp Fiction', 'genre': 'Crime', 'duration': 154, 'description': "The lives of two mob hitmen, a boxer, a gangster's wife, and a pair of diner bandits intertwine in four tales of violence and redemption.", 'image_url': 'https://m.media-amazon.com/images/M/MV5BNGNhMDIzZTUtNTBlZi00MTRlLWFjM2ItYzViMjE3YzI5MjljXkEyXkFqcGdeQXVyNzkwMjQ5NzM@._V1_.jpg', 'release': '1994-00-00', 'director_id': 1, 'cast': [2, 3, 4, 5]}
Name validation Pulp Fiction
Genre validation Crime
Description validation The lives of two mob hitmen, a boxer, a gangster's wife, and a pair of diner bandits intertwine in four tales of violence and redemption.
Bad Request: /site-admin/add-film/
[04/May/2024 00:36:34] "POST /site-admin/add-film/ HTTP/1.1" 400 197

关于如何解决这个问题有什么 idea 吗?

推荐答案

因为ModelSerializer 会将release字段映射到DateField.这样,就针对框架设置的default input date formats进行验证.

为了覆盖它,您需要设置自己的有效输入列表settings.py

DATE_INPUT_FORMATS = [
    "%Y",
    "%Y-%m",
    "%m-%Y",
    "%Y-%m-%d",
    "%d-%m-%Y",
]

serializers.py

class FilmSerializer(serializers.ModelSerializer):
    release = serializers.DateField(input_formats=settings.DATE_INPUT_FORMATS)

    class Meta:
        model = Film
        fields = "__all__"

Python相关问答推荐

是否有使用纯霍夫曼编码的现代图像格式?

尽管进程输出错误消息,subProcess.check_call的CalledProcess错误.stderr为无

在两极中实施频率编码

如何匹配3D圆柱体的轴和半径?

Tkinter滑动条标签.我不确定如何删除滑动块标签或更改其文本

使用Ubuntu、Python和Weasyprint的Docker文件-venv的问题

配置Sweetviz以分析对象类型列,而无需转换

将特定列信息移动到当前行下的新行

如何检测背景有噪的图像中的正方形

max_of_three使用First_select、second_select、

如何将Docker内部运行的mariadb与主机上Docker外部运行的Python脚本连接起来

Telethon加入私有频道

有没有一种方法可以从python的pussompy比较结果中提取文本?

DataFrames与NaN的条件乘法

将pandas导出到CSV数据,但在此之前,将日期按最小到最大排序

使用特定值作为引用替换数据框行上的值

如何防止Pandas将索引标为周期?

Python—转换日期:价目表到新行

剪切间隔以添加特定日期

搜索按钮不工作,Python tkinter