Fix schema generation with OneToOneFields (#81)
* Fix: OneToOneRel, used by OneToOneField doesn't have help_text nor primary_key attributes, thus breaking OpenAPISchemaGenerator; use hasattr() as safe-guard. * Fix: use getattr() with a default value instead of hasattr() + acessing the value * Add: 'people' app that breaks drf_yasg without previous commits * Update tests/references.yaml + run isort and flake8 * Fix: set on_delete for Person.identity as Django-2+ requires itopenapi3
parent
a7fbba4967
commit
309a6eb8cd
|
|
@ -409,9 +409,9 @@ class OpenAPISchemaGenerator(object):
|
||||||
if getattr(view_cls, 'lookup_field', None) == variable and attrs['type'] == openapi.TYPE_STRING:
|
if getattr(view_cls, 'lookup_field', None) == variable and attrs['type'] == openapi.TYPE_STRING:
|
||||||
attrs['pattern'] = getattr(view_cls, 'lookup_value_regex', attrs.get('pattern', None))
|
attrs['pattern'] = getattr(view_cls, 'lookup_value_regex', attrs.get('pattern', None))
|
||||||
|
|
||||||
if model_field and model_field.help_text:
|
if model_field and getattr(model_field, 'help_text', False):
|
||||||
description = force_text(model_field.help_text)
|
description = force_text(model_field.help_text)
|
||||||
elif model_field and model_field.primary_key:
|
elif model_field and getattr(model_field, 'primary_key', False):
|
||||||
description = get_pk_description(model, model_field)
|
description = get_pk_description(model, model_field)
|
||||||
else:
|
else:
|
||||||
description = None
|
description = None
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PeopleConfig(AppConfig):
|
||||||
|
name = 'people'
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.10 on 2018-03-18 16:22
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Identity',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('firstName', models.CharField(max_length=30, null=True)),
|
||||||
|
('lastName', models.CharField(max_length=30, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Person',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('Identity', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='person', to='people.Identity')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.10 on 2018-03-18 17:04
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('people', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='person',
|
||||||
|
name='Identity',
|
||||||
|
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='person', to='people.Identity'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Identity(models.Model):
|
||||||
|
firstName = models.CharField(max_length=30, null=True)
|
||||||
|
lastName = models.CharField(max_length=30, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Person(models.Model):
|
||||||
|
Identity = models.OneToOneField(Identity, related_name='person',
|
||||||
|
on_delete=models.PROTECT)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Identity, Person
|
||||||
|
|
||||||
|
|
||||||
|
class IdentitySerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Identity
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class PersonSerializer(serializers.ModelSerializer):
|
||||||
|
identity = IdentitySerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Person
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
identity = Identity(**validated_data['identity'])
|
||||||
|
identity.save()
|
||||||
|
validated_data['identity'] = identity
|
||||||
|
return super().create(validated_data)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from .views import IdentityViewSet, PersonViewSet
|
||||||
|
|
||||||
|
person_list = PersonViewSet.as_view({
|
||||||
|
'get': 'list',
|
||||||
|
'post': 'create'
|
||||||
|
})
|
||||||
|
person_detail = PersonViewSet.as_view({
|
||||||
|
'get': 'retrieve',
|
||||||
|
'patch': 'partial_update',
|
||||||
|
'delete': 'destroy'
|
||||||
|
})
|
||||||
|
|
||||||
|
identity_detail = IdentityViewSet.as_view({
|
||||||
|
'get': 'retrieve',
|
||||||
|
'patch': 'partial_update',
|
||||||
|
})
|
||||||
|
|
||||||
|
urlpatterns = (
|
||||||
|
url(r'^$', person_list, name='people-list'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)$', person_detail, name='person-detail'),
|
||||||
|
|
||||||
|
url(r'^(?P<person>[0-9]+)/identity$', identity_detail,
|
||||||
|
name='person-identity'),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Identity, Person
|
||||||
|
from .serializers import IdentitySerializer, PersonSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class PersonViewSet(viewsets.ModelViewSet):
|
||||||
|
model = Person
|
||||||
|
queryset = Person.objects
|
||||||
|
serializer_class = PersonSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class IdentityViewSet(viewsets.ModelViewSet):
|
||||||
|
model = Identity
|
||||||
|
queryset = Identity.objects
|
||||||
|
serializer_class = IdentitySerializer
|
||||||
|
|
@ -27,6 +27,7 @@ INSTALLED_APPS = [
|
||||||
'users',
|
'users',
|
||||||
'articles',
|
'articles',
|
||||||
'todo',
|
'todo',
|
||||||
|
'people'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
|
||||||
|
|
@ -63,5 +63,6 @@ urlpatterns = [
|
||||||
url(r'^articles/', include('articles.urls')),
|
url(r'^articles/', include('articles.urls')),
|
||||||
url(r'^users/', include('users.urls')),
|
url(r'^users/', include('users.urls')),
|
||||||
url(r'^todo/', include('todo.urls')),
|
url(r'^todo/', include('todo.urls')),
|
||||||
|
url(r'^people/', include('people.urls')),
|
||||||
url(r'^plain/', plain_view),
|
url(r'^plain/', plain_view),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,113 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
format: slug
|
format: slug
|
||||||
pattern: '[a-z0-9]+(?:-[a-z0-9]+)'
|
pattern: '[a-z0-9]+(?:-[a-z0-9]+)'
|
||||||
|
/people/:
|
||||||
|
get:
|
||||||
|
operationId: people_list
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Person'
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
post:
|
||||||
|
operationId: people_create
|
||||||
|
description: ''
|
||||||
|
parameters:
|
||||||
|
- name: data
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Person'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Person'
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
parameters: []
|
||||||
|
/people/{id}:
|
||||||
|
get:
|
||||||
|
operationId: people_read
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Person'
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
patch:
|
||||||
|
operationId: people_partial_update
|
||||||
|
description: ''
|
||||||
|
parameters:
|
||||||
|
- name: data
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Person'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Person'
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
delete:
|
||||||
|
operationId: people_delete
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: ''
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
description: A unique integer value identifying this person.
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
/people/{person}/identity:
|
||||||
|
get:
|
||||||
|
operationId: people_identity_read
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Identity'
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
patch:
|
||||||
|
operationId: people_identity_partial_update
|
||||||
|
description: ''
|
||||||
|
parameters:
|
||||||
|
- name: data
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Identity'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Identity'
|
||||||
|
tags:
|
||||||
|
- people
|
||||||
|
parameters:
|
||||||
|
- name: person
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
/plain/:
|
/plain/:
|
||||||
get:
|
get:
|
||||||
operationId: plain_list
|
operationId: plain_list
|
||||||
|
|
@ -609,6 +716,37 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
Identity:
|
||||||
|
title: Identity
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
title: ID
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
firstName:
|
||||||
|
title: FirstName
|
||||||
|
type: string
|
||||||
|
maxLength: 30
|
||||||
|
lastName:
|
||||||
|
title: LastName
|
||||||
|
type: string
|
||||||
|
maxLength: 30
|
||||||
|
readOnly: true
|
||||||
|
Person:
|
||||||
|
required:
|
||||||
|
- Identity
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
title: ID
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
identity:
|
||||||
|
$ref: '#/definitions/Identity'
|
||||||
|
Identity:
|
||||||
|
title: Identity
|
||||||
|
type: integer
|
||||||
Project:
|
Project:
|
||||||
required:
|
required:
|
||||||
- projectName
|
- projectName
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue