We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
File System Exporter should have an option to generate listing files
**Is your feature request related to a problem? Please describe.**
When Katello uses the File System exporter it does not generate the listing files in CDN directory structure. Katello does not directly have access to the pulp's content directories. Due to this it is not able to easily generate `Listing` files. It relies right now on the hammer export command being run from the same machine as pulp.

**Describe the solution you'd like**
Pulp exporter should ideally have an option or a flag to generate `listing` files when fs exporter is run.

**Describe alternatives you've considered**
Couple of things to consider here 
- This is only relevant to YUM/KS repos
- This probably only useful for certain Yum repos. But a flag in the exporter would take care of that.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pulpcore/app/serializers/content.py`
Content:
```
1 import hashlib
2 from gettext import gettext as _
3 
4 from django.db import transaction
5 from rest_framework import serializers
6 from rest_framework.validators import UniqueValidator
7 
8 from pulpcore.app import models
9 from pulpcore.app.serializers import base, fields
10 
11 
12 class BaseContentSerializer(base.ModelSerializer):
13     pulp_href = base.DetailIdentityField(view_name_pattern=r"contents(-.*/.*)-detail")
14 
15     class Meta:
16         model = models.Content
17         fields = base.ModelSerializer.Meta.fields
18 
19 
20 class NoArtifactContentSerializer(BaseContentSerializer):
21     class Meta:
22         model = models.Content
23         fields = BaseContentSerializer.Meta.fields
24 
25 
26 class SingleArtifactContentSerializer(BaseContentSerializer):
27     artifact = fields.SingleContentArtifactField(
28         help_text=_("Artifact file representing the physical content"),
29     )
30 
31     relative_path = serializers.CharField(
32         help_text=_("Path where the artifact is located relative to distributions base_path"),
33         validators=[fields.relative_path_validator],
34         write_only=True,
35     )
36 
37     def __init__(self, *args, **kwargs):
38         """
39         Initializer for SingleArtifactContentSerializer
40         """
41         super().__init__(*args, **kwargs)
42 
43         # If the content model has its own database field 'relative_path',
44         # we should not mark the field write_only
45         if hasattr(self.Meta.model, "relative_path") and "relative_path" in self.fields:
46             self.fields["relative_path"].write_only = False
47 
48     @transaction.atomic
49     def create(self, validated_data):
50         """
51         Create the content and associate it with its Artifact.
52 
53         Args:
54             validated_data (dict): Data to save to the database
55         """
56         artifact = validated_data.pop("artifact")
57         if "relative_path" not in self.fields or self.fields["relative_path"].write_only:
58             relative_path = validated_data.pop("relative_path")
59         else:
60             relative_path = validated_data.get("relative_path")
61         content = self.Meta.model.objects.create(**validated_data)
62         models.ContentArtifact.objects.create(
63             artifact=artifact, content=content, relative_path=relative_path
64         )
65         return content
66 
67     class Meta:
68         model = models.Content
69         fields = BaseContentSerializer.Meta.fields + ("artifact", "relative_path")
70 
71 
72 class MultipleArtifactContentSerializer(BaseContentSerializer):
73     artifacts = fields.ContentArtifactsField(
74         help_text=_(
75             "A dict mapping relative paths inside the Content to the corresponding"
76             "Artifact URLs. E.g.: {'relative/path': "
77             "'/artifacts/1/'"
78         ),
79     )
80 
81     @transaction.atomic
82     def create(self, validated_data):
83         """
84         Create the content and associate it with all its Artifacts.
85 
86         Args:
87             validated_data (dict): Data to save to the database
88         """
89         artifacts = validated_data.pop("artifacts")
90         content = self.Meta.model.objects.create(**validated_data)
91         for relative_path, artifact in artifacts.items():
92             models.ContentArtifact.objects.create(
93                 artifact=artifact, content=content, relative_path=relative_path
94             )
95         return content
96 
97     class Meta:
98         model = models.Content
99         fields = BaseContentSerializer.Meta.fields + ("artifacts",)
100 
101 
102 class ContentChecksumSerializer(serializers.Serializer):
103     """
104     Provide a serializer with artifact checksum fields for single artifact content.
105 
106     If you use this serializer, it's recommended that you prefetch artifacts:
107 
108         Content.objects.prefetch_related("_artifacts").all()
109     """
110 
111     md5 = fields.ContentArtifactChecksumField(
112         help_text=_("The MD5 checksum if available."),
113         checksum="md5",
114     )
115 
116     sha1 = fields.ContentArtifactChecksumField(
117         help_text=_("The SHA-1 checksum if available."),
118         checksum="sha1",
119     )
120 
121     sha224 = fields.ContentArtifactChecksumField(
122         help_text=_("The SHA-224 checksum if available."),
123         checksum="sha224",
124     )
125 
126     sha256 = fields.ContentArtifactChecksumField(
127         help_text=_("The SHA-256 checksum if available."),
128         checksum="sha256",
129     )
130 
131     sha384 = fields.ContentArtifactChecksumField(
132         help_text=_("The SHA-384 checksum if available."),
133         checksum="sha384",
134     )
135 
136     sha512 = fields.ContentArtifactChecksumField(
137         help_text=_("The SHA-512 checksum if available."),
138         checksum="sha512",
139     )
140 
141     class Meta:
142         model = models.Content
143         fields = base.ModelSerializer.Meta.fields + (
144             "md5",
145             "sha1",
146             "sha224",
147             "sha256",
148             "sha384",
149             "sha512",
150         )
151 
152 
153 class ArtifactSerializer(base.ModelSerializer):
154     pulp_href = base.IdentityField(view_name="artifacts-detail")
155 
156     file = serializers.FileField(help_text=_("The stored file."), allow_empty_file=True)
157 
158     size = serializers.IntegerField(help_text=_("The size of the file in bytes."), required=False)
159 
160     md5 = serializers.CharField(
161         help_text=_("The MD5 checksum of the file if available."), required=False, allow_null=True
162     )
163 
164     sha1 = serializers.CharField(
165         help_text=_("The SHA-1 checksum of the file if available."),
166         required=False,
167         allow_null=True,
168     )
169 
170     sha224 = serializers.CharField(
171         help_text=_("The SHA-224 checksum of the file if available."),
172         required=False,
173         allow_null=True,
174     )
175 
176     sha256 = serializers.CharField(
177         help_text=_("The SHA-256 checksum of the file if available."),
178         required=False,
179         allow_null=True,
180     )
181 
182     sha384 = serializers.CharField(
183         help_text=_("The SHA-384 checksum of the file if available."),
184         required=False,
185         allow_null=True,
186     )
187 
188     sha512 = serializers.CharField(
189         help_text=_("The SHA-512 checksum of the file if available."),
190         required=False,
191         allow_null=True,
192     )
193 
194     def validate(self, data):
195         """
196         Validate file by size and by all checksums provided.
197 
198         Args:
199             data (:class:`django.http.QueryDict`): QueryDict mapping Artifact model fields to their
200                 values
201 
202         Raises:
203             :class:`rest_framework.exceptions.ValidationError`: When the expected file size or any
204                 of the checksums don't match their actual values.
205         """
206         super().validate(data)
207         if "size" in data:
208             if data["file"].size != int(data["size"]):
209                 raise serializers.ValidationError(_("The size did not match actual size of file."))
210         else:
211             data["size"] = data["file"].size
212 
213         bad_algs = []
214         for algorithm in models.Artifact.FORBIDDEN_DIGESTS:
215             if algorithm in data:
216                 bad_algs.append(algorithm)
217         if bad_algs:
218             raise serializers.ValidationError(
219                 _(f"Checksum algorithms {bad_algs} forbidden for this Pulp instance.")
220             )
221 
222         for algorithm in hashlib.algorithms_guaranteed:
223             if algorithm in models.Artifact.DIGEST_FIELDS:
224                 digest = data["file"].hashers[algorithm].hexdigest()
225 
226                 if algorithm in data and digest != data[algorithm]:
227                     raise serializers.ValidationError(
228                         _("The %s checksum did not match.") % algorithm
229                     )
230                 else:
231                     data[algorithm] = digest
232                 if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:
233                     validator = UniqueValidator(
234                         models.Artifact.objects.all(),
235                         message=_("{0} checksum must be unique.").format(algorithm),
236                     )
237                     validator.instance = None
238 
239                     validator(digest, self.fields[algorithm])
240         return data
241 
242     class Meta:
243         model = models.Artifact
244         fields = base.ModelSerializer.Meta.fields + (
245             "file",
246             "size",
247             "md5",
248             "sha1",
249             "sha224",
250             "sha256",
251             "sha384",
252             "sha512",
253         )
254 
255 
256 class SigningServiceSerializer(base.ModelSerializer):
257     """
258     A serializer for the model declaring a signing service.
259     """
260 
261     pulp_href = base.IdentityField(view_name="signing-services-detail")
262     name = serializers.CharField(help_text=_("A unique name used to recognize a script."))
263     script = serializers.CharField(
264         help_text=_("An absolute path to a script which is going to be used for the signing.")
265     )
266 
267     class Meta:
268         model = models.SigningService
269         fields = BaseContentSerializer.Meta.fields + ("name", "script")
270
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pulpcore/app/serializers/content.py b/pulpcore/app/serializers/content.py
--- a/pulpcore/app/serializers/content.py
+++ b/pulpcore/app/serializers/content.py
@@ -1,4 +1,3 @@
-import hashlib
 from gettext import gettext as _
 
 from django.db import transaction
@@ -219,24 +218,24 @@
                 _(f"Checksum algorithms {bad_algs} forbidden for this Pulp instance.")
             )
 
-        for algorithm in hashlib.algorithms_guaranteed:
-            if algorithm in models.Artifact.DIGEST_FIELDS:
-                digest = data["file"].hashers[algorithm].hexdigest()
-
-                if algorithm in data and digest != data[algorithm]:
-                    raise serializers.ValidationError(
-                        _("The %s checksum did not match.") % algorithm
-                    )
-                else:
-                    data[algorithm] = digest
-                if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:
-                    validator = UniqueValidator(
-                        models.Artifact.objects.all(),
-                        message=_("{0} checksum must be unique.").format(algorithm),
-                    )
-                    validator.instance = None
-
-                    validator(digest, self.fields[algorithm])
+        for algorithm in reversed(models.Artifact.DIGEST_FIELDS):
+            digest = data["file"].hashers[algorithm].hexdigest()
+
+            if algorithm in data and digest != data[algorithm]:
+                raise serializers.ValidationError(_("The %s checksum did not match.") % algorithm)
+            else:
+                data[algorithm] = digest
+
+            if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:
+                validator = UniqueValidator(
+                    models.Artifact.objects.all(),
+                    message=_("Artifact with {0} checksum of '{1}' already exists.").format(
+                        algorithm, digest
+                    ),
+                )
+                validator.instance = None
+                validator(digest, self.fields[algorithm])
+
         return data
 
     class Meta:

{"golden_diff": "diff --git a/pulpcore/app/serializers/content.py b/pulpcore/app/serializers/content.py\n--- a/pulpcore/app/serializers/content.py\n+++ b/pulpcore/app/serializers/content.py\n@@ -1,4 +1,3 @@\n-import hashlib\n from gettext import gettext as _\n \n from django.db import transaction\n@@ -219,24 +218,24 @@\n                 _(f\"Checksum algorithms {bad_algs} forbidden for this Pulp instance.\")\n             )\n \n-        for algorithm in hashlib.algorithms_guaranteed:\n-            if algorithm in models.Artifact.DIGEST_FIELDS:\n-                digest = data[\"file\"].hashers[algorithm].hexdigest()\n-\n-                if algorithm in data and digest != data[algorithm]:\n-                    raise serializers.ValidationError(\n-                        _(\"The %s checksum did not match.\") % algorithm\n-                    )\n-                else:\n-                    data[algorithm] = digest\n-                if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:\n-                    validator = UniqueValidator(\n-                        models.Artifact.objects.all(),\n-                        message=_(\"{0} checksum must be unique.\").format(algorithm),\n-                    )\n-                    validator.instance = None\n-\n-                    validator(digest, self.fields[algorithm])\n+        for algorithm in reversed(models.Artifact.DIGEST_FIELDS):\n+            digest = data[\"file\"].hashers[algorithm].hexdigest()\n+\n+            if algorithm in data and digest != data[algorithm]:\n+                raise serializers.ValidationError(_(\"The %s checksum did not match.\") % algorithm)\n+            else:\n+                data[algorithm] = digest\n+\n+            if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:\n+                validator = UniqueValidator(\n+                    models.Artifact.objects.all(),\n+                    message=_(\"Artifact with {0} checksum of '{1}' already exists.\").format(\n+                        algorithm, digest\n+                    ),\n+                )\n+                validator.instance = None\n+                validator(digest, self.fields[algorithm])\n+\n         return data\n \n     class Meta:\n", "issue": "File System Exporter should have an option to generate listing files\n**Is your feature request related to a problem? Please describe.**\r\nWhen Katello uses the File System exporter it does not generate the listing files in CDN directory structure. Katello does not directly have access to the pulp's content directories. Due to this it is not able to easily generate `Listing` files. It relies right now on the hammer export command being run from the same machine as pulp.\r\n\r\n**Describe the solution you'd like**\r\nPulp exporter should ideally have an option or a flag to generate `listing` files when fs exporter is run.\r\n\r\n**Describe alternatives you've considered**\r\nCouple of things to consider here \r\n- This is only relevant to YUM/KS repos\r\n- This probably only useful for certain Yum repos. But a flag in the exporter would take care of that.\r\n\r\n\n", "before_files": [{"content": "import hashlib\nfrom gettext import gettext as _\n\nfrom django.db import transaction\nfrom rest_framework import serializers\nfrom rest_framework.validators import UniqueValidator\n\nfrom pulpcore.app import models\nfrom pulpcore.app.serializers import base, fields\n\n\nclass BaseContentSerializer(base.ModelSerializer):\n    pulp_href = base.DetailIdentityField(view_name_pattern=r\"contents(-.*/.*)-detail\")\n\n    class Meta:\n        model = models.Content\n        fields = base.ModelSerializer.Meta.fields\n\n\nclass NoArtifactContentSerializer(BaseContentSerializer):\n    class Meta:\n        model = models.Content\n        fields = BaseContentSerializer.Meta.fields\n\n\nclass SingleArtifactContentSerializer(BaseContentSerializer):\n    artifact = fields.SingleContentArtifactField(\n        help_text=_(\"Artifact file representing the physical content\"),\n    )\n\n    relative_path = serializers.CharField(\n        help_text=_(\"Path where the artifact is located relative to distributions base_path\"),\n        validators=[fields.relative_path_validator],\n        write_only=True,\n    )\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Initializer for SingleArtifactContentSerializer\n        \"\"\"\n        super().__init__(*args, **kwargs)\n\n        # If the content model has its own database field 'relative_path',\n        # we should not mark the field write_only\n        if hasattr(self.Meta.model, \"relative_path\") and \"relative_path\" in self.fields:\n            self.fields[\"relative_path\"].write_only = False\n\n    @transaction.atomic\n    def create(self, validated_data):\n        \"\"\"\n        Create the content and associate it with its Artifact.\n\n        Args:\n            validated_data (dict): Data to save to the database\n        \"\"\"\n        artifact = validated_data.pop(\"artifact\")\n        if \"relative_path\" not in self.fields or self.fields[\"relative_path\"].write_only:\n            relative_path = validated_data.pop(\"relative_path\")\n        else:\n            relative_path = validated_data.get(\"relative_path\")\n        content = self.Meta.model.objects.create(**validated_data)\n        models.ContentArtifact.objects.create(\n            artifact=artifact, content=content, relative_path=relative_path\n        )\n        return content\n\n    class Meta:\n        model = models.Content\n        fields = BaseContentSerializer.Meta.fields + (\"artifact\", \"relative_path\")\n\n\nclass MultipleArtifactContentSerializer(BaseContentSerializer):\n    artifacts = fields.ContentArtifactsField(\n        help_text=_(\n            \"A dict mapping relative paths inside the Content to the corresponding\"\n            \"Artifact URLs. E.g.: {'relative/path': \"\n            \"'/artifacts/1/'\"\n        ),\n    )\n\n    @transaction.atomic\n    def create(self, validated_data):\n        \"\"\"\n        Create the content and associate it with all its Artifacts.\n\n        Args:\n            validated_data (dict): Data to save to the database\n        \"\"\"\n        artifacts = validated_data.pop(\"artifacts\")\n        content = self.Meta.model.objects.create(**validated_data)\n        for relative_path, artifact in artifacts.items():\n            models.ContentArtifact.objects.create(\n                artifact=artifact, content=content, relative_path=relative_path\n            )\n        return content\n\n    class Meta:\n        model = models.Content\n        fields = BaseContentSerializer.Meta.fields + (\"artifacts\",)\n\n\nclass ContentChecksumSerializer(serializers.Serializer):\n    \"\"\"\n    Provide a serializer with artifact checksum fields for single artifact content.\n\n    If you use this serializer, it's recommended that you prefetch artifacts:\n\n        Content.objects.prefetch_related(\"_artifacts\").all()\n    \"\"\"\n\n    md5 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The MD5 checksum if available.\"),\n        checksum=\"md5\",\n    )\n\n    sha1 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-1 checksum if available.\"),\n        checksum=\"sha1\",\n    )\n\n    sha224 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-224 checksum if available.\"),\n        checksum=\"sha224\",\n    )\n\n    sha256 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-256 checksum if available.\"),\n        checksum=\"sha256\",\n    )\n\n    sha384 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-384 checksum if available.\"),\n        checksum=\"sha384\",\n    )\n\n    sha512 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-512 checksum if available.\"),\n        checksum=\"sha512\",\n    )\n\n    class Meta:\n        model = models.Content\n        fields = base.ModelSerializer.Meta.fields + (\n            \"md5\",\n            \"sha1\",\n            \"sha224\",\n            \"sha256\",\n            \"sha384\",\n            \"sha512\",\n        )\n\n\nclass ArtifactSerializer(base.ModelSerializer):\n    pulp_href = base.IdentityField(view_name=\"artifacts-detail\")\n\n    file = serializers.FileField(help_text=_(\"The stored file.\"), allow_empty_file=True)\n\n    size = serializers.IntegerField(help_text=_(\"The size of the file in bytes.\"), required=False)\n\n    md5 = serializers.CharField(\n        help_text=_(\"The MD5 checksum of the file if available.\"), required=False, allow_null=True\n    )\n\n    sha1 = serializers.CharField(\n        help_text=_(\"The SHA-1 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha224 = serializers.CharField(\n        help_text=_(\"The SHA-224 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha256 = serializers.CharField(\n        help_text=_(\"The SHA-256 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha384 = serializers.CharField(\n        help_text=_(\"The SHA-384 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha512 = serializers.CharField(\n        help_text=_(\"The SHA-512 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    def validate(self, data):\n        \"\"\"\n        Validate file by size and by all checksums provided.\n\n        Args:\n            data (:class:`django.http.QueryDict`): QueryDict mapping Artifact model fields to their\n                values\n\n        Raises:\n            :class:`rest_framework.exceptions.ValidationError`: When the expected file size or any\n                of the checksums don't match their actual values.\n        \"\"\"\n        super().validate(data)\n        if \"size\" in data:\n            if data[\"file\"].size != int(data[\"size\"]):\n                raise serializers.ValidationError(_(\"The size did not match actual size of file.\"))\n        else:\n            data[\"size\"] = data[\"file\"].size\n\n        bad_algs = []\n        for algorithm in models.Artifact.FORBIDDEN_DIGESTS:\n            if algorithm in data:\n                bad_algs.append(algorithm)\n        if bad_algs:\n            raise serializers.ValidationError(\n                _(f\"Checksum algorithms {bad_algs} forbidden for this Pulp instance.\")\n            )\n\n        for algorithm in hashlib.algorithms_guaranteed:\n            if algorithm in models.Artifact.DIGEST_FIELDS:\n                digest = data[\"file\"].hashers[algorithm].hexdigest()\n\n                if algorithm in data and digest != data[algorithm]:\n                    raise serializers.ValidationError(\n                        _(\"The %s checksum did not match.\") % algorithm\n                    )\n                else:\n                    data[algorithm] = digest\n                if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:\n                    validator = UniqueValidator(\n                        models.Artifact.objects.all(),\n                        message=_(\"{0} checksum must be unique.\").format(algorithm),\n                    )\n                    validator.instance = None\n\n                    validator(digest, self.fields[algorithm])\n        return data\n\n    class Meta:\n        model = models.Artifact\n        fields = base.ModelSerializer.Meta.fields + (\n            \"file\",\n            \"size\",\n            \"md5\",\n            \"sha1\",\n            \"sha224\",\n            \"sha256\",\n            \"sha384\",\n            \"sha512\",\n        )\n\n\nclass SigningServiceSerializer(base.ModelSerializer):\n    \"\"\"\n    A serializer for the model declaring a signing service.\n    \"\"\"\n\n    pulp_href = base.IdentityField(view_name=\"signing-services-detail\")\n    name = serializers.CharField(help_text=_(\"A unique name used to recognize a script.\"))\n    script = serializers.CharField(\n        help_text=_(\"An absolute path to a script which is going to be used for the signing.\")\n    )\n\n    class Meta:\n        model = models.SigningService\n        fields = BaseContentSerializer.Meta.fields + (\"name\", \"script\")\n", "path": "pulpcore/app/serializers/content.py"}], "after_files": [{"content": "from gettext import gettext as _\n\nfrom django.db import transaction\nfrom rest_framework import serializers\nfrom rest_framework.validators import UniqueValidator\n\nfrom pulpcore.app import models\nfrom pulpcore.app.serializers import base, fields\n\n\nclass BaseContentSerializer(base.ModelSerializer):\n    pulp_href = base.DetailIdentityField(view_name_pattern=r\"contents(-.*/.*)-detail\")\n\n    class Meta:\n        model = models.Content\n        fields = base.ModelSerializer.Meta.fields\n\n\nclass NoArtifactContentSerializer(BaseContentSerializer):\n    class Meta:\n        model = models.Content\n        fields = BaseContentSerializer.Meta.fields\n\n\nclass SingleArtifactContentSerializer(BaseContentSerializer):\n    artifact = fields.SingleContentArtifactField(\n        help_text=_(\"Artifact file representing the physical content\"),\n    )\n\n    relative_path = serializers.CharField(\n        help_text=_(\"Path where the artifact is located relative to distributions base_path\"),\n        validators=[fields.relative_path_validator],\n        write_only=True,\n    )\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Initializer for SingleArtifactContentSerializer\n        \"\"\"\n        super().__init__(*args, **kwargs)\n\n        # If the content model has its own database field 'relative_path',\n        # we should not mark the field write_only\n        if hasattr(self.Meta.model, \"relative_path\") and \"relative_path\" in self.fields:\n            self.fields[\"relative_path\"].write_only = False\n\n    @transaction.atomic\n    def create(self, validated_data):\n        \"\"\"\n        Create the content and associate it with its Artifact.\n\n        Args:\n            validated_data (dict): Data to save to the database\n        \"\"\"\n        artifact = validated_data.pop(\"artifact\")\n        if \"relative_path\" not in self.fields or self.fields[\"relative_path\"].write_only:\n            relative_path = validated_data.pop(\"relative_path\")\n        else:\n            relative_path = validated_data.get(\"relative_path\")\n        content = self.Meta.model.objects.create(**validated_data)\n        models.ContentArtifact.objects.create(\n            artifact=artifact, content=content, relative_path=relative_path\n        )\n        return content\n\n    class Meta:\n        model = models.Content\n        fields = BaseContentSerializer.Meta.fields + (\"artifact\", \"relative_path\")\n\n\nclass MultipleArtifactContentSerializer(BaseContentSerializer):\n    artifacts = fields.ContentArtifactsField(\n        help_text=_(\n            \"A dict mapping relative paths inside the Content to the corresponding\"\n            \"Artifact URLs. E.g.: {'relative/path': \"\n            \"'/artifacts/1/'\"\n        ),\n    )\n\n    @transaction.atomic\n    def create(self, validated_data):\n        \"\"\"\n        Create the content and associate it with all its Artifacts.\n\n        Args:\n            validated_data (dict): Data to save to the database\n        \"\"\"\n        artifacts = validated_data.pop(\"artifacts\")\n        content = self.Meta.model.objects.create(**validated_data)\n        for relative_path, artifact in artifacts.items():\n            models.ContentArtifact.objects.create(\n                artifact=artifact, content=content, relative_path=relative_path\n            )\n        return content\n\n    class Meta:\n        model = models.Content\n        fields = BaseContentSerializer.Meta.fields + (\"artifacts\",)\n\n\nclass ContentChecksumSerializer(serializers.Serializer):\n    \"\"\"\n    Provide a serializer with artifact checksum fields for single artifact content.\n\n    If you use this serializer, it's recommended that you prefetch artifacts:\n\n        Content.objects.prefetch_related(\"_artifacts\").all()\n    \"\"\"\n\n    md5 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The MD5 checksum if available.\"),\n        checksum=\"md5\",\n    )\n\n    sha1 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-1 checksum if available.\"),\n        checksum=\"sha1\",\n    )\n\n    sha224 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-224 checksum if available.\"),\n        checksum=\"sha224\",\n    )\n\n    sha256 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-256 checksum if available.\"),\n        checksum=\"sha256\",\n    )\n\n    sha384 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-384 checksum if available.\"),\n        checksum=\"sha384\",\n    )\n\n    sha512 = fields.ContentArtifactChecksumField(\n        help_text=_(\"The SHA-512 checksum if available.\"),\n        checksum=\"sha512\",\n    )\n\n    class Meta:\n        model = models.Content\n        fields = base.ModelSerializer.Meta.fields + (\n            \"md5\",\n            \"sha1\",\n            \"sha224\",\n            \"sha256\",\n            \"sha384\",\n            \"sha512\",\n        )\n\n\nclass ArtifactSerializer(base.ModelSerializer):\n    pulp_href = base.IdentityField(view_name=\"artifacts-detail\")\n\n    file = serializers.FileField(help_text=_(\"The stored file.\"), allow_empty_file=True)\n\n    size = serializers.IntegerField(help_text=_(\"The size of the file in bytes.\"), required=False)\n\n    md5 = serializers.CharField(\n        help_text=_(\"The MD5 checksum of the file if available.\"), required=False, allow_null=True\n    )\n\n    sha1 = serializers.CharField(\n        help_text=_(\"The SHA-1 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha224 = serializers.CharField(\n        help_text=_(\"The SHA-224 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha256 = serializers.CharField(\n        help_text=_(\"The SHA-256 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha384 = serializers.CharField(\n        help_text=_(\"The SHA-384 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    sha512 = serializers.CharField(\n        help_text=_(\"The SHA-512 checksum of the file if available.\"),\n        required=False,\n        allow_null=True,\n    )\n\n    def validate(self, data):\n        \"\"\"\n        Validate file by size and by all checksums provided.\n\n        Args:\n            data (:class:`django.http.QueryDict`): QueryDict mapping Artifact model fields to their\n                values\n\n        Raises:\n            :class:`rest_framework.exceptions.ValidationError`: When the expected file size or any\n                of the checksums don't match their actual values.\n        \"\"\"\n        super().validate(data)\n        if \"size\" in data:\n            if data[\"file\"].size != int(data[\"size\"]):\n                raise serializers.ValidationError(_(\"The size did not match actual size of file.\"))\n        else:\n            data[\"size\"] = data[\"file\"].size\n\n        bad_algs = []\n        for algorithm in models.Artifact.FORBIDDEN_DIGESTS:\n            if algorithm in data:\n                bad_algs.append(algorithm)\n        if bad_algs:\n            raise serializers.ValidationError(\n                _(f\"Checksum algorithms {bad_algs} forbidden for this Pulp instance.\")\n            )\n\n        for algorithm in reversed(models.Artifact.DIGEST_FIELDS):\n            digest = data[\"file\"].hashers[algorithm].hexdigest()\n\n            if algorithm in data and digest != data[algorithm]:\n                raise serializers.ValidationError(_(\"The %s checksum did not match.\") % algorithm)\n            else:\n                data[algorithm] = digest\n\n            if algorithm in models.Artifact.RELIABLE_DIGEST_FIELDS:\n                validator = UniqueValidator(\n                    models.Artifact.objects.all(),\n                    message=_(\"Artifact with {0} checksum of '{1}' already exists.\").format(\n                        algorithm, digest\n                    ),\n                )\n                validator.instance = None\n                validator(digest, self.fields[algorithm])\n\n        return data\n\n    class Meta:\n        model = models.Artifact\n        fields = base.ModelSerializer.Meta.fields + (\n            \"file\",\n            \"size\",\n            \"md5\",\n            \"sha1\",\n            \"sha224\",\n            \"sha256\",\n            \"sha384\",\n            \"sha512\",\n        )\n\n\nclass SigningServiceSerializer(base.ModelSerializer):\n    \"\"\"\n    A serializer for the model declaring a signing service.\n    \"\"\"\n\n    pulp_href = base.IdentityField(view_name=\"signing-services-detail\")\n    name = serializers.CharField(help_text=_(\"A unique name used to recognize a script.\"))\n    script = serializers.CharField(\n        help_text=_(\"An absolute path to a script which is going to be used for the signing.\")\n    )\n\n    class Meta:\n        model = models.SigningService\n        fields = BaseContentSerializer.Meta.fields + (\"name\", \"script\")\n", "path": "pulpcore/app/serializers/content.py"}]}