56 | {% block session_auth_button %}
57 | {% csrf_token %}
58 |
59 | {% block user_context_message %}
60 | {% if request.user.is_authenticated %}
61 |
62 | Django {{ request.user }}
64 |
65 | {% endif %}
66 | {% endblock %}
67 |
68 | {% if request.user.is_authenticated %}
69 |
70 |
75 |
76 | {% else %}
77 |
78 |
83 |
84 | {% endif %}
85 | {% endblock %}
86 |
87 | {% endif %}
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/testproj/articles/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axnsan12/drf-yasg/a4ced8d51404be7b5ca04268f955c6898496d37f/testproj/articles/__init__.py
--------------------------------------------------------------------------------
/testproj/articles/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.1 on 2018-03-18 18:32
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import uuid
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='Article',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('title', models.CharField(help_text='title model help_text', max_length=255, unique=True)),
23 | ('body', models.TextField(help_text='article model help_text', max_length=5000)),
24 | ('slug', models.SlugField(blank=True, help_text='slug model help_text', unique=True)),
25 | ('date_created', models.DateTimeField(auto_now_add=True)),
26 | ('date_modified', models.DateTimeField(auto_now=True)),
27 | ('article_type', models.PositiveSmallIntegerField(choices=[(1, 'first'), (2, 'second'), (3, 'third'), (7, 'seven'), (8, 'eight')], help_text='IntegerField declared on model with choices=(...) and exposed via ModelSerializer', null=True)),
28 | ('cover', models.ImageField(blank=True, upload_to='article/original/')),
29 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='articles', to=settings.AUTH_USER_MODEL)),
30 | ],
31 | ),
32 | migrations.CreateModel(
33 | name='ArticleGroup',
34 | fields=[
35 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36 | ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
37 | ('title', models.CharField(help_text='title model help_text', max_length=255, unique=True)),
38 | ('slug', models.SlugField(blank=True, help_text='slug model help_text', unique=True)),
39 | ],
40 | ),
41 | migrations.AddField(
42 | model_name='article',
43 | name='group',
44 | field=models.ForeignKey(blank=True, default=None, on_delete=django.db.models.deletion.PROTECT, related_name='articles_as_main', to='articles.ArticleGroup'),
45 | ),
46 | migrations.AddField(
47 | model_name='article',
48 | name='original_group',
49 | field=models.ForeignKey(blank=True, default=None, on_delete=django.db.models.deletion.PROTECT, related_name='articles_as_original', to='articles.ArticleGroup'),
50 | ),
51 | ]
52 |
--------------------------------------------------------------------------------
/testproj/articles/migrations/0002_article_read_only_nullable.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-03-02 03:51
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('articles', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='article',
15 | name='read_only_nullable',
16 | field=models.CharField(blank=True, max_length=20, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/testproj/articles/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axnsan12/drf-yasg/a4ced8d51404be7b5ca04268f955c6898496d37f/testproj/articles/migrations/__init__.py
--------------------------------------------------------------------------------
/testproj/articles/models.py:
--------------------------------------------------------------------------------
1 | import uuid
2 |
3 | from django.db import models
4 |
5 |
6 | class Article(models.Model):
7 | title = models.CharField(
8 | help_text="title model help_text", max_length=255, blank=False, unique=True
9 | )
10 | body = models.TextField(
11 | help_text="article model help_text", max_length=5000, blank=False
12 | )
13 | slug = models.SlugField(help_text="slug model help_text", unique=True, blank=True)
14 | date_created = models.DateTimeField(auto_now_add=True)
15 | date_modified = models.DateTimeField(auto_now=True)
16 | author = models.ForeignKey(
17 | "auth.User", related_name="articles", on_delete=models.CASCADE
18 | )
19 | article_type = models.PositiveSmallIntegerField(
20 | help_text=(
21 | "IntegerField declared on model with choices=(...) and exposed via "
22 | "ModelSerializer"
23 | ),
24 | choices=((1, "first"), (2, "second"), (3, "third"), (7, "seven"), (8, "eight")),
25 | null=True,
26 | )
27 |
28 | cover = models.ImageField(upload_to="article/original/", blank=True)
29 | group = models.ForeignKey(
30 | "ArticleGroup",
31 | related_name="articles_as_main",
32 | blank=True,
33 | default=None,
34 | on_delete=models.PROTECT,
35 | )
36 | original_group = models.ForeignKey(
37 | "ArticleGroup",
38 | related_name="articles_as_original",
39 | blank=True,
40 | default=None,
41 | on_delete=models.PROTECT,
42 | )
43 | read_only_nullable = models.CharField(max_length=20, null=True, blank=True)
44 |
45 |
46 | class ArticleGroup(models.Model):
47 | uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
48 |
49 | title = models.CharField(
50 | help_text="title model help_text", max_length=255, blank=False, unique=True
51 | )
52 | slug = models.SlugField(help_text="slug model help_text", unique=True, blank=True)
53 |
--------------------------------------------------------------------------------
/testproj/articles/serializers.py:
--------------------------------------------------------------------------------
1 | from django.utils.translation import gettext_lazy as _
2 | from rest_framework import serializers
3 |
4 | from articles.models import Article, ArticleGroup
5 |
6 |
7 | class ArticleSerializer(serializers.ModelSerializer):
8 | references = serializers.DictField(
9 | help_text=_("this is a really bad example"),
10 | child=serializers.URLField(
11 | help_text="but i needed to test these 2 fields somehow"
12 | ),
13 | read_only=True,
14 | )
15 | uuid = serializers.UUIDField(
16 | help_text="should articles have UUIDs?", read_only=True
17 | )
18 | cover_name = serializers.FileField(use_url=False, source="cover", required=True)
19 | group = serializers.SlugRelatedField(
20 | slug_field="uuid", queryset=ArticleGroup.objects.all()
21 | )
22 | original_group = serializers.SlugRelatedField(slug_field="uuid", read_only=True)
23 |
24 | class Meta:
25 | model = Article
26 | fields = (
27 | "title",
28 | "author",
29 | "body",
30 | "slug",
31 | "date_created",
32 | "date_modified",
33 | "read_only_nullable",
34 | "references",
35 | "uuid",
36 | "cover",
37 | "cover_name",
38 | "article_type",
39 | "group",
40 | "original_group",
41 | )
42 | read_only_fields = (
43 | "date_created",
44 | "date_modified",
45 | "references",
46 | "uuid",
47 | "cover_name",
48 | "read_only_nullable",
49 | )
50 | lookup_field = "slug"
51 | extra_kwargs = {
52 | "body": {"help_text": "body serializer help_text"},
53 | "author": {
54 | "default": serializers.CurrentUserDefault(),
55 | "help_text": _(
56 | "The ID of the user that created this article; if none is "
57 | "provided, defaults to the currently logged in user."
58 | ),
59 | },
60 | "read_only_nullable": {"allow_null": True},
61 | }
62 |
63 |
64 | class ImageUploadSerializer(serializers.Serializer):
65 | image_id = serializers.UUIDField(read_only=True)
66 | what_am_i_doing = serializers.RegexField(
67 | regex=r"^69$", help_text="test", default="69", allow_null=True
68 | )
69 | image_styles = serializers.ListSerializer(
70 | child=serializers.ChoiceField(choices=["wide", "tall", "thumb", "social"]),
71 | help_text="Parameter with Items",
72 | )
73 | upload = serializers.ImageField(help_text="image serializer help_text")
74 |
--------------------------------------------------------------------------------
/testproj/articles/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import include, path
2 | from rest_framework.routers import SimpleRouter
3 |
4 | from articles import views
5 |
6 | router = SimpleRouter()
7 | router.register("", views.ArticleViewSet)
8 |
9 | urlpatterns = [
10 | path("", include(router.urls)),
11 | ]
12 |
--------------------------------------------------------------------------------
/testproj/articles/views.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from django.utils.decorators import method_decorator
4 | from django_filters.rest_framework import DjangoFilterBackend
5 | from rest_framework import viewsets
6 | from rest_framework.filters import OrderingFilter
7 | from rest_framework.pagination import LimitOffsetPagination
8 | from rest_framework.parsers import FileUploadParser, MultiPartParser
9 | from rest_framework.response import Response
10 |
11 | from articles import serializers
12 | from articles.models import Article
13 | from drf_yasg import openapi
14 | from drf_yasg.app_settings import swagger_settings
15 | from drf_yasg.inspectors import (
16 | DrfAPICompatInspector,
17 | FieldInspector,
18 | NotHandled,
19 | SwaggerAutoSchema,
20 | )
21 | from drf_yasg.utils import no_body, swagger_auto_schema
22 |
23 |
24 | class DjangoFilterDescriptionInspector(DrfAPICompatInspector):
25 | def get_filter_parameters(self, filter_backend):
26 | if isinstance(filter_backend, DjangoFilterBackend):
27 | result = super(
28 | DjangoFilterDescriptionInspector, self
29 | ).get_filter_parameters(filter_backend)
30 | for param in result:
31 | if (
32 | not param.get("description", "")
33 | or param.get("description") == param.name
34 | ):
35 | param.description = (
36 | "Filter the returned list by {field_name}".format(
37 | field_name=param.name
38 | )
39 | )
40 |
41 | return result
42 |
43 | return NotHandled
44 |
45 |
46 | class NoSchemaTitleInspector(FieldInspector):
47 | def process_result(self, result, method_name, obj, **kwargs):
48 | # remove the `title` attribute of all Schema objects
49 | if isinstance(result, openapi.Schema.OR_REF):
50 | # traverse any references and alter the Schema object in place
51 | schema = openapi.resolve_ref(result, self.components)
52 | schema.pop("title", None)
53 |
54 | # no ``return schema`` here, because it would mean we always generate
55 | # an inline `object` instead of a definition reference
56 |
57 | # return back the same object that we got - i.e. a reference if we got a
58 | # reference
59 | return result
60 |
61 |
62 | class NoTitleAutoSchema(SwaggerAutoSchema):
63 | field_inspectors = [
64 | NoSchemaTitleInspector
65 | ] + swagger_settings.DEFAULT_FIELD_INSPECTORS
66 |
67 |
68 | class NoPagingAutoSchema(NoTitleAutoSchema):
69 | def should_page(self):
70 | return False
71 |
72 |
73 | class ArticlePagination(LimitOffsetPagination):
74 | default_limit = 5
75 | max_limit = 25
76 |
77 |
78 | @method_decorator(
79 | name="list",
80 | decorator=swagger_auto_schema(
81 | operation_description=(
82 | "description from swagger_auto_schema via method_decorator"
83 | ),
84 | filter_inspectors=[DjangoFilterDescriptionInspector],
85 | ),
86 | )
87 | class ArticleViewSet(viewsets.ModelViewSet):
88 | """
89 | ArticleViewSet class docstring
90 |
91 | retrieve:
92 | retrieve class docstring
93 |
94 | destroy:
95 | destroy class docstring
96 |
97 | partial_update:
98 | partial_update class docstring
99 | """
100 |
101 | queryset = Article.objects.all()
102 | lookup_field = "slug"
103 | lookup_value_regex = r"[a-z0-9]+(?:-[a-z0-9]+)"
104 | serializer_class = serializers.ArticleSerializer
105 |
106 | pagination_class = ArticlePagination
107 | filter_backends = (DjangoFilterBackend, OrderingFilter)
108 | filterset_fields = ("title",)
109 | # django-filter 1.1 compatibility; was renamed to filterset_fields in 2.0
110 | # TODO: remove when dropping support for Django 1.11
111 | filter_fields = filterset_fields
112 | ordering_fields = ("date_modified", "date_created")
113 | ordering = ("date_created",)
114 |
115 | swagger_schema = NoTitleAutoSchema
116 |
117 | from rest_framework.decorators import action
118 |
119 | @swagger_auto_schema(
120 | auto_schema=NoPagingAutoSchema,
121 | filter_inspectors=[DjangoFilterDescriptionInspector],
122 | )
123 | @action(detail=False, methods=["get"])
124 | def today(self, request):
125 | today_min = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
126 | today_max = datetime.datetime.combine(datetime.date.today(), datetime.time.max)
127 | articles = (
128 | self.get_queryset().filter(date_created__range=(today_min, today_max)).all()
129 | )
130 | serializer = self.serializer_class(articles, many=True)
131 | return Response(serializer.data)
132 |
133 | @swagger_auto_schema(
134 | method="get", operation_description="image GET description override"
135 | )
136 | @swagger_auto_schema(method="post", request_body=serializers.ImageUploadSerializer)
137 | @swagger_auto_schema(
138 | method="delete",
139 | manual_parameters=[
140 | openapi.Parameter(
141 | name="delete_form_param",
142 | in_=openapi.IN_FORM,
143 | type=openapi.TYPE_INTEGER,
144 | description="this should not crash (form parameter on DELETE method)",
145 | )
146 | ],
147 | )
148 | @action(
149 | detail=True,
150 | methods=["get", "post", "delete"],
151 | parser_classes=(MultiPartParser, FileUploadParser),
152 | )
153 | def image(self, request, slug=None):
154 | """
155 | image method docstring
156 | """
157 | pass
158 |
159 | @swagger_auto_schema(request_body=no_body, operation_id="no_body_test")
160 | def update(self, request, *args, **kwargs):
161 | """update method docstring"""
162 | return super(ArticleViewSet, self).update(request, *args, **kwargs)
163 |
164 | @swagger_auto_schema(
165 | operation_description="partial_update description override",
166 | responses={404: "slug not found"},
167 | operation_summary="partial_update summary",
168 | deprecated=True,
169 | )
170 | def partial_update(self, request, *args, **kwargs):
171 | """partial_update method docstring"""
172 | return super(ArticleViewSet, self).partial_update(request, *args, **kwargs)
173 |
174 | def destroy(self, request, *args, **kwargs):
175 | """destroy method docstring"""
176 | return super(ArticleViewSet, self).destroy(request, *args, **kwargs)
177 |
--------------------------------------------------------------------------------
/testproj/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproj.settings.local")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | try:
11 | import django # noqa: F401
12 | except ImportError:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | )
18 | raise
19 | execute_from_command_line(sys.argv)
20 |
--------------------------------------------------------------------------------
/testproj/people/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axnsan12/drf-yasg/a4ced8d51404be7b5ca04268f955c6898496d37f/testproj/people/__init__.py
--------------------------------------------------------------------------------
/testproj/people/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class PeopleConfig(AppConfig):
5 | name = "people"
6 |
--------------------------------------------------------------------------------
/testproj/people/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.1 on 2018-03-18 18:32
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Identity',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('firstName', models.CharField(max_length=30, null=True)),
20 | ('lastName', models.CharField(max_length=30, null=True)),
21 | ],
22 | ),
23 | migrations.CreateModel(
24 | name='Person',
25 | fields=[
26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
27 | ('identity', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='person', to='people.Identity')),
28 | ],
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/testproj/people/migrations/0002_rename_identity_fields.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-08-06 13:34
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('people', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='identity',
15 | name='lastName',
16 | field=models.CharField(help_text="