Categorías
Python

Emulando sentencias switch / case en Python

 

Tabla de Contenidos

  • Objetivos
  • Por qué Django Framework RESTO?
  • Django Configuración del Proyecto
  • Django aplicación y configuración marco RESTO
  • base de datos y configuración Modelo
  • cordura Compruebe Estructura
  • Serializadores
  • REST Rutas
  • y Pruebas (TDD) GET
  • navegable API
  • RoutesGET ALLGET SinglePOSTPUTDELETE
  • TODO
  • LLEGAR individual
  • POSTAL
  • PUT
  • BORRAR
  • Conclusión y próximos pasos
  • obtener todos
  • LLEGAR individual
  • POSTAL
  • PUT
  • BORRAR

Este post guía por el proceso de el desarrollo de un API REST basada en CRUD con Django y Django REST Marco, que se utiliza para crear rápidamente APIs REST basados ​​en modelos Django.

Esta aplicación utiliza:

  • Python V3.6.0
  • Django v1.11.0 v3.6.2
  • Django marco RESTO
  • Postgres v9.6.1
  • psycopg2 v2.7.1 Bono

gratuito: Haga clic aquí para descargar una copia de los «Ejemplos» API REST Guía y obtener una introducción práctica a principios del API Python + REST con ejemplos de acciones concretas.

NOTA: Confirmar el tercer curso real del pitón para un tutorial más en profundidad sobre Django marco RESTO.

Objetivos

Al final de este tutorial serás capaz de … ¿Por qué

Django Marco de descanso?

Django RESTO marco REST (marco) ofrece una serie de características de gran alcance fuera de la caja que van bien con idiomática Django, incluyendo:

Además, la documentación es fácil de leer y lleno de ejemplos. Si usted está construyendo una API REST donde se tiene una relación de uno a uno entre los puntos finales de API y sus modelos, luego descansar Marco es el camino a seguir.

Django Configuración del Proyecto

crear y activar una virtualenv:

$ mkdir django-puppy-store
$ cd django-puppy-store
$ python3.6 -m venv env
$ source env/bin/activate

Instalar Django y configurar un nuevo proyecto:

(env)$ pip install django==1.11.0
(env)$ django-admin startproject puppy_store

Su estructura actual proyecto debe tener este aspecto:

└── puppy_store
├── manage.py
└── puppy_store
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py

Django aplicación y configuración marco RESTO

Empiece por la creación de la aplicación y la instalación de los cachorros marco RESTO dentro de su virtualenv:

(env)$ cd puppy_store
(env)$ python manage.py startapp puppies
(env)$ pip install djangorestframework==3.6.2

Ahora tenemos que configurar nuestro proyecto Django para hacer uso de marco RESTO.

En primer lugar, añadir la aplicación cachorros y rest_framework a la sección INSTALLED_APPS dentro puppy_store / puppy_store / settings.py :

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'puppies',
'rest_framework'
]

A continuación, definir la configuración global para Marco descansar en un solo diccionario, de nuevo, en el settings.py archivo:

REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [],
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

Esto permite el acceso sin restricciones a la API y establece el formato de la prueba por defecto a JSON para todas las solicitudes.

NOTA: No restringido el acceso está muy bien para el desarrollo local, pero en un entorno de producción puede que tenga que restringir el acceso a determinados parámetros. Asegúrese de actualizar esto. Revisar la documentación para obtener más información.

Su estructura actual proyecto debe parecerse a:

└── puppy_store
├── manage.py
├── puppies
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── puppy_store
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py

base de datos y un conjunto de modelos de configuración

amainó la base de datos PostgreSQL y aplicar todas las migraciones a la misma.

NOTA : No dude en permuta a cabo Postgres para la base de datos relacional de su elección!

Una vez que tenga un servidor Postgres trabajar en su sistema, abra la consola interactiva Postgres y crear la base de datos:

$ psql
# CREATE DATABASE puppy_store_drf;
CREATE DATABASE
# q

Instalar psycopg2 para que podamos interactuar con el servidor PostgreSQL a través de Python:

(env)$ pip install psycopg2==2.7.1

actualización de la configuración de la base de datos de configuración .py , añadiendo el nombre de usuario y la contraseña apropiada:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'puppy_store_drf',
'USER': '',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': '5432'
}
}

a continuación, definir un modelo cachorro con algunos atributos básicos en Django-puppy-store / puppy_store / cachorros / models.py :

from django.db import models

class Puppy(models.Model):
"""
Puppy Model
Defines the attributes of a puppy
"""
name = models.CharField(max_length=255)
age = models.IntegerField()
breed = models.CharField(max_length=255)
color = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def get_breed(self):
return self.name + ' belongs to ' + self.breed + ' breed.'

def __repr__(self):
return self.name + ' is added.'

Ahora se aplica la migración:

(env)$ python manage.py makemigrations
(env)$ python manage.py migrate

cordura Compruebe

Hop en psql nuevamente y verifique que el puppies_puppy ha sido creado:

$ psql
# c puppy_store_drf
You are now connected to database "puppy_store_drf".
puppy_store_drf=# dt
List of relations
Schema | Name | Type | Owner
--------+----------------------------+-------+----------------
public | auth_group | table | michael.herman
public | auth_group_permissions | table | michael.herman
public | auth_permission | table | michael.herman
public | auth_user | table | michael.herman
public | auth_user_groups | table | michael.herman
public | auth_user_user_permissions | table | michael.herman
public | django_admin_log | table | michael.herman
public | django_content_type | table | michael.herman
public | django_migrations | table | michael.herman
public | django_session | table | michael.herman
public | puppies_puppy | table | michael.herman
(11 rows)

NOTA: puede ejecutar d + puppies_puppy si desea ver los detalles reales de la tabla.

Antes de continuar, vamos a escribir una prueba unitaria rápida para el modelo Puppy.

Agregue el código siguiente a un nuevo archivo llamado test_models.py en una nueva carpeta llamada “pruebas” dentro “django-puppy-store / puppy_store / cachorros”:

from django.test import TestCase
from ..models import Puppy

class PuppyTest(TestCase):
""" Test module for Puppy model """

def setUp(self):
Puppy.objects.create(
name='Casper', age=3, breed='Bull Dog', color='Black')
Puppy.objects.create(
name='Muffin', age=1, breed='Gradane', color='Brown')

def test_puppy_breed(self):
puppy_casper = Puppy.objects.get(name='Casper')
puppy_muffin = Puppy.objects.get(name='Muffin')
self.assertEqual(
puppy_casper.get_breed(), "Casper belongs to Bull Dog breed.")
self.assertEqual(
puppy_muffin.get_breed(), "Muffin belongs to Gradane breed.")

En la prueba anterior, hemos añadido entradas ficticias en nuestra mesa cachorro a través del método de configuración () desde django.test.TestCase y afirmó que el método get_breed () devuelve la cadena correcta.

Añadir archivo un __init__.py a “pruebas” y eliminar el archivo tests.py de “Django-puppy-store / puppy_store / cachorros”.

vamos a ejecutar nuestra primera prueba:

(env)$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.007s

OK
Destroying test database for alias 'default'...

Gran! Nuestra primera prueba de la unidad ha pasado!

Serializadores

Antes de pasar a la creación de la API actual, vamos a definir un serializador para nuestro modelo cachorro que valida los QuerySets modelo y produce los tipos de datos Pythonic a trabajar.

Añadir el siguiente fragmento de django-puppy-store / puppy_store / cachorros / serializers.py :

from rest_framework import serializers
from .models import Puppy

class PuppySerializer(serializers.ModelSerializer):
class Meta:
model = Puppy
fields = ('name', 'age', 'breed', 'color', 'created_at', 'updated_at')

En el anterior fragmento de código se definió una ModelSerializer para nuestro modelo cachorro, validando todos los campos mencionados. En pocas palabras, si usted tiene una relación de uno a uno entre los puntos finales de API y sus modelos – que probablemente debería Si va a crear una API REST – entonces usted puede utilizar un ModelSerializer para crear un Serializador.

Con nuestra base de datos en su lugar, ahora puede empezar a construir la API REST … Estructura

REST

En una API REST, los puntos finales (URL) definen la estructura de la API y cómo los usuarios finales acceso a los datos de nuestra aplicación utilizando el protocolo HTTP métodos – GET, POST, PUT, DELETE. Los puntos finales deben ser organizados lógicamente en torno colecciones y elementos , ambos de los cuales son los recursos.

En nuestro caso, tenemos un único recurso, cachorros, por lo que vamos a utilizar las siguientes direcciones URL – / cachorros / y / cachorros / para las colecciones y elementos, respectivamente: Rutas

y Pruebas (TDD)

Nosotros va a tomar un enfoque de prueba de primer lugar de un enfoque basado en pruebas a fondo, en el que vamos a ir a través del siguiente proceso:

  • añadir una prueba de unidad, solo código suficiente para dejar
  • luego actualizar el código para hacerlo pasar la prueba.

Una vez que los pases de prueba, empezar de nuevo con el mismo proceso para la nueva prueba.

Comience por crear un nuevo archivo, django-puppy-store / puppy_store / cachorros / pruebas / test_views.py , para contener todas las pruebas para nuestros puntos de vista y crear un nuevo cliente de prueba para nuestra aplicación:

import json
from rest_framework import status
from django.test import TestCase, Client
from django.urls import reverse
from ..models import Puppy
from ..serializers import PuppySerializer

# initialize the APIClient app
client = Client()

Antes de comenzar con todas las rutas de la API, primero vamos a crear un esqueleto de todas las funciones de vista que devuelven respuestas vacías y el mapa con sus URLs apropiados dentro de la django-puppy-store / puppy_store / cachorros / views.py archivo:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Puppy
from .serializers import PuppySerializer

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
return Response({})
# delete a single puppy
elif request.method == 'DELETE':
return Response({})
# update details of a single puppy
elif request.method == 'PUT':
return Response({})

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
return Response({})
# insert a new record for a puppy
elif request.method == 'POST':
return Response({})

Crear la URL respectivas para que coincida con los puntos de vista en django-puppy-store / puppy_store / cachorros / urls.py :

from django.conf.urls import url
from . import views

urlpatterns = [
url(
r'^api/v1/puppies/(?P[0-9]+)$',
views.get_delete_update_puppy,
name='get_delete_update_puppy'
),
url(
r'^api/v1/puppies/$',
views.get_post_puppies,
name='get_post_puppies'
)
]

actualización django-puppy-store / puppy_store / puppy_store / urls.py así: API

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
url(r'^', include('puppies.urls')),
url(
r'^api-auth/',
include('rest_framework.urls', namespace='rest_framework')
),
url(r'^admin/', admin.site.urls),
]

navegable

con todas las rutas cableadas ahora con las funciones de vista, vamos a abrir API REST navegable del marco interfaceand verificar si todas las URLs están funcionando como se esperaba.

En primer lugar, el fuego hasta el servidor de desarrollo:

(env)$ python manage.py runserver

Asegúrese de comentar todos los atributos en la sección REST_FRAMEWORK de nuestro archivo settings.py, a bypasslogin. Ahora visite http: // localhost: 8000 / api / v1 / cachorros

verá un diseño HTML interactivo para la respuesta de la API. Del mismo modo podemos probar las otras direcciones URL y verificar todas las direcciones URL están trabajando perfectamente bien. inicio de

Vamos con nuestra unidad de prueba para cada ruta. Rutas

obtener todos

de inicio con una prueba para verificar los registros inverosímiles:

class GetAllPuppiesTest(TestCase):
""" Test module for GET all puppies API """

def setUp(self):
Puppy.objects.create(
name='Casper', age=3, breed='Bull Dog', color='Black')
Puppy.objects.create(
name='Muffin', age=1, breed='Gradane', color='Brown')
Puppy.objects.create(
name='Rambo', age=2, breed='Labrador', color='Black')
Puppy.objects.create(
name='Ricky', age=6, breed='Labrador', color='Brown')

def test_get_all_puppies(self):
# get API response
response = client.get(reverse('get_post_puppies'))
# get data from db
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

Ejecutar la prueba. Debería ver el siguiente error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != [OrderedDict([('name', 'Casper'), ('age',[687 chars])])]

actualización de la vista para obtener la prueba para pasar.

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
return Response(serializer.data)
# insert a new record for a puppy
elif request.method == 'POST':
return Response({})

Aquí, obtenemos todos los registros para los cachorros y validar cada uno usando el PuppySerializer.

Ejecutar las pruebas para asegurarse de que todo pase:

Ran 2 tests in 0.072s

OK

LLEGAR individual

Fetching un solo cachorro implica dos casos de prueba:

Añadir las pruebas:

class GetSinglePuppyTest(TestCase):
""" Test module for GET single puppy API """

def setUp(self):
self.casper = Puppy.objects.create(
name='Casper', age=3, breed='Bull Dog', color='Black')
self.muffin = Puppy.objects.create(
name='Muffin', age=1, breed='Gradane', color='Brown')
self.rambo = Puppy.objects.create(
name='Rambo', age=2, breed='Labrador', color='Black')
self.ricky = Puppy.objects.create(
name='Ricky', age=6, breed='Labrador', color='Brown')

def test_get_valid_single_puppy(self):
response = client.get(
reverse('get_delete_update_puppy', kwargs={'pk': self.rambo.pk}))
puppy = Puppy.objects.get(pk=self.rambo.pk)
serializer = PuppySerializer(puppy)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_get_invalid_single_puppy(self):
response = client.get(
reverse('get_delete_update_puppy', kwargs={'pk': 30}))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Ejecutar las pruebas. Debería ver el siguiente error:

actualización self.assertEqual(response.data, serializer.data)
AssertionError: {} != {'name': 'Rambo', 'age': 2, 'breed': 'Labr[109 chars]26Z'}
la vista:

@api_view(['GET', 'UPDATE', 'DELETE'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
serializer = PuppySerializer(puppy)
return Response(serializer.data)

En el fragmento anterior, obtenemos el cachorro utilizando un ID. Ejecutar las pruebas para asegurarse de que todo pase.

POSTAL

la inserción de un nuevo registro implica dos casos, así:

En primer lugar, las pruebas de escritura sobre éste:

class CreateNewPuppyTest(TestCase):
""" Test module for inserting a new puppy """

def setUp(self):
self.valid_payload = {
'name': 'Muffin',
'age': 4,
'breed': 'Pamerion',
'color': 'White'
}
self.invalid_payload = {
'name': '',
'age': 4,
'breed': 'Pamerion',
'color': 'White'
}

def test_create_valid_puppy(self):
response = client.post(
reverse('get_post_puppies'),
data=json.dumps(self.valid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_create_invalid_puppy(self):
response = client.post(
reverse('get_post_puppies'),
data=json.dumps(self.invalid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Ejecutar las pruebas. Verá que hay dos fallos:

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 200 != 400

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 200 != 201

Una vez más, actualice el fin de obtener las pruebas que pasan:

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
return Response(serializer.data)
# insert a new record for a puppy
if request.method == 'POST':
data = {
'name': request.data.get('name'),
'age': int(request.data.get('age')),
'breed': request.data.get('breed'),
'color': request.data.get('color')
}
serializer = PuppySerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Aquí, inserta un nuevo registro serializando y validación de los datos de la solicitud antes de insertar a la base de datos.

ejecutar las pruebas de nuevo para asegurarse de que pasan.

También puede probar esto con la API navegable. Pon en marcha el servidor de desarrollo de nuevo, y vaya a http: // localhost: 8000 / api / V1 / cachorros /. Luego, dentro de la forma de POST, presentar la siguiente como application / json:

{
"name": "Muffin",
"age": 4,
"breed": "Pamerion",
"color": "White"
}

Asegúrese de que el GET ALL y conseguir trabajo individual también.

PUT

de inicio con una prueba a actualizar un registro. Al igual que en la adición de un registro, es necesario volver a prueba para ambos cambios válidos y no válidos:

class UpdateSinglePuppyTest(TestCase):
""" Test module for updating an existing puppy record """

def setUp(self):
self.casper = Puppy.objects.create(
name='Casper', age=3, breed='Bull Dog', color='Black')
self.muffin = Puppy.objects.create(
name='Muffy', age=1, breed='Gradane', color='Brown')
self.valid_payload = {
'name': 'Muffy',
'age': 2,
'breed': 'Labrador',
'color': 'Black'
}
self.invalid_payload = {
'name': '',
'age': 4,
'breed': 'Pamerion',
'color': 'White'
}

def test_valid_update_puppy(self):
response = client.put(
reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
data=json.dumps(self.valid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_invalid_update_puppy(self):
response = client.put(
reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
data=json.dumps(self.invalid_payload),
content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Ejecutar las pruebas.

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 405 != 400

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 405 != 204

actualizar la vista:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
serializer = PuppySerializer(puppy)
return Response(serializer.data)

# update details of a single puppy
if request.method == 'PUT':
serializer = PuppySerializer(puppy, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# delete a single puppy
elif request.method == 'DELETE':
return Response({})

En el fragmento anterior, similar a una inserción, que serializar y validar los datos de la solicitud y luego responder de manera adecuada.

Ejecutar las pruebas de nuevo para asegurarse de que pasan todas las pruebas.

BORRAR

Para borrar un solo registro, se requiere un ID:

class DeleteSinglePuppyTest(TestCase):
""" Test module for deleting an existing puppy record """

def setUp(self):
self.casper = Puppy.objects.create(
name='Casper', age=3, breed='Bull Dog', color='Black')
self.muffin = Puppy.objects.create(
name='Muffy', age=1, breed='Gradane', color='Brown')

def test_valid_delete_puppy(self):
response = client.delete(
reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}))
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_invalid_delete_puppy(self):
response = client.delete(
reverse('get_delete_update_puppy', kwargs={'pk': 30}))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Ejecutar las pruebas. Debería ver:

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 200 != 204

actualizar la vista:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
serializer = PuppySerializer(puppy)
return Response(serializer.data)

# update details of a single puppy
if request.method == 'PUT':
serializer = PuppySerializer(puppy, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# delete a single puppy
if request.method == 'DELETE':
puppy.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

Ejecutar las pruebas de nuevo. Asegúrese de que todos ellos pasar. Asegúrese de probar la actualización y la funcionalidad de eliminación dentro de la API navegable, así!

Conclusión y próximos pasos

En este tutorial, que pasaron por el proceso de creación de una API REST utilizando Django marco descanso con un enfoque de prueba primero. Bono

gratuito: Haga clic aquí para descargar una copia del «reposo en una cáscara de nuez» guía con una introducción práctica a los principios y ejemplos de la API REST.

¿Qué sigue? Para hacer nuestra API REST sólida y segura, podemos implementar permisos y estrangular a un entorno de producción para permitir el acceso restringido sobre la base de las credenciales de autenticación y limitación de velocidad para evitar cualquier tipo de ataque DDoS. Además, no se olvide de prevenir la API navegable de ser accesible en un entorno de producción.

No dude en compartir sus comentarios, preguntas o sugerencias en los comentarios a continuación. El código completo se puede encontrar en el repositorio django-puppy-tienda.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *