diff --git a/AUTHORS b/AUTHORS index 02c1f34..264d841 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,15 +5,15 @@ Project Leader: - Valeryi Savich -Contributors of code for aiorest-ws are: +Contributors: - Dmitry Vechorko -The serializers, field classes and exception style was taken from the Django -REST Framework. The original code is BSD licensed. The following copyrights -apply: +The serializers, field, exception classes and documentation for them was +taken from the Django REST Framework. The original code is BSD licensed. The +following copyrights apply: -- (c) 2015 Tom Christie +- (c) 2016 Tom Christie The to_xml() function was taken from the django-rest-framework-xml module. The original code is BSD licensed with the following copyrights from that @@ -24,21 +24,21 @@ module: The routing classes and URL parser code originally placed in aiohttp library. The original code is Apache 2.0 licensed. The following copyrights apply: -- (c) 2015 Andrew Svetlov +- (c) 2016 Andrew Svetlov The View, MethodViewMeta, MethodBasedView classes are partly modified, and the base implementation was taken from the Flask project (views module). The original code is BSD licensed with the following copyrights from that module: -- (c) 2015 Armin Ronacher +- (c) 2016 Armin Ronacher The idea for the "global" settings with further overriding, some util modules for processing requests and accessing to fields was taken from the Django Framework. The original code is BSD licensed with the following copyrights from that module: -- (c) 2015 Django Software Foundation +- (c) 2016 Django Software Foundation The nice_repr (renamed to humanize_timedelta), iso8601_repr and parse (renamed to parse_timedelta) functions for processing timedelta objects and diff --git a/README.md b/README.md index db9af05..2135638 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Features Requirements ----- -- Python >= 3.4.0 -- Autobahn.ws >= 0.12.1 +- Python >= 3.4.2 +- Autobahn.ws == 0.16.0 Optional: - SQLAlchemy ORM >= 1.0 @@ -54,8 +54,7 @@ Getting started var ws = null; var isopen = false; -window.onload = function() -{ +window.onload = function() { ws = new WebSocket("ws://127.0.0.1:8080"); ws.onopen = function() { console.log("Connected!"); @@ -103,7 +102,7 @@ class HelloClientProtocol(WebSocketClientProtocol): if __name__ == '__main__': - factory = WebSocketClientFactory("ws://localhost:8080", debug=False) + factory = WebSocketClientFactory("ws://localhost:8080") factory.protocol = HelloClientProtocol loop = asyncio.get_event_loop() diff --git a/aiorest_ws/db/orm/serializers.py b/aiorest_ws/db/orm/serializers.py index 877dab7..b5ac47c 100644 --- a/aiorest_ws/db/orm/serializers.py +++ b/aiorest_ws/db/orm/serializers.py @@ -11,7 +11,7 @@ from aiorest_ws.exceptions import ImproperlyConfigured from aiorest_ws.db.orm.abstract import AbstractSerializer, AbstractField, \ empty, SkipField -from aiorest_ws.db.orm.fields import HiddenField +from aiorest_ws.db.orm.fields import * # NOQA from aiorest_ws.db.orm.exceptions import ValidationError from aiorest_ws.utils.fields import set_value, get_attribute from aiorest_ws.utils.functional import cached_property diff --git a/docs/source/app.rst b/docs/source/app.rst index c6ec952..df2938a 100644 --- a/docs/source/app.rst +++ b/docs/source/app.rst @@ -22,7 +22,6 @@ handler. It can be method-based: from aiorest_ws.views import MethodBasedView class HelloWorld(MethodBasedView): - def get(self, request, *args, **kwargs): return "Hello, world!" diff --git a/docs/source/conf.py b/docs/source/conf.py index 3073fda..00c6167 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -69,7 +69,7 @@ # General information about the project. project = 'aiorest-ws' -copyright = '2015, Valeryi Savich' +copyright = '2016, Valeryi Savich' author = 'Valeryi Savich' # The version info for the project you're documenting, acts as replacement for @@ -77,9 +77,9 @@ # built documents. # # The short X.Y version. -version = '1.0' +version = '1.1' # The full version, including alpha/beta/rc tags. -release = '1.0' +release = '1.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -136,6 +136,7 @@ # further. For a list of options available for each theme, see the # documentation. html_theme_options = { + 'logo': 'logo.png', 'description': 'REST framework with WebSockets support', 'github_user': 'Relrin', @@ -172,7 +173,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ['static', ] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/source/index.rst b/docs/source/index.rst index 0c60b64..2a695a0 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,6 +22,7 @@ Features - Authentication (using JSON Web Token) - Customizing behaviour of your application through settings file - Compressing messages for minimize of transmitted traffic (if your browser support) +- Model serializing for Django and SQLAlchemy ORM - SSL support Library installation @@ -43,8 +44,7 @@ Client example (JavaScript): var ws = null; var isopen = false; - window.onload = function() - { + window.onload = function() { ws = new WebSocket("ws://127.0.0.1:8080"); ws.onopen = function() { console.log("Connected!"); @@ -80,7 +80,6 @@ Client example (Python): from autobahn.asyncio.websocket import WebSocketClientProtocol, \ WebSocketClientFactory - class HelloClientProtocol(WebSocketClientProtocol): def onOpen(self): @@ -93,7 +92,7 @@ Client example (Python): if __name__ == '__main__': - factory = WebSocketClientFactory("ws://localhost:8080", debug=False) + factory = WebSocketClientFactory("ws://localhost:8080") factory.protocol = HelloClientProtocol loop = asyncio.get_event_loop() @@ -157,15 +156,15 @@ Contents: --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 4 auth app request routing + serializing wrappers views - api Indices and tables ================== diff --git a/docs/source/serializing.rst b/docs/source/serializing.rst new file mode 100644 index 0000000..8f32798 --- /dev/null +++ b/docs/source/serializing.rst @@ -0,0 +1,679 @@ +.. _aiorest-ws-serializing: + +Model serializing +================= + +.. currentmodule:: aiorest_ws.db.orm + +Serializers which will be described below give to a programmer opportunities to convert complex +data structures like model instances of certain ORM to the default Python datatypes. The result +of this processing can be returned to a user in JSON, XML or any other formats. These serializers +also provide deserializing mechanisms which can be used for parsing and validating user input data +that will be used for to work with ORMs further. + +These serializers largely based on ideas and concepts of Django REST Framework. In this way a lot +of functionality that will be described further will coincide with this library. + +At the moment aiorest-ws have support with the following ORMs: + +- Django +- SQLAlchemy + +You can find corresponding modules for each of mentioned ORMs in ``aiorest_ws.db.orm.*`` namespace. + +Model serializers +----------------- +The aiorest-ws library provides :class:`ModelSerializer` classes which can be used for serializing +(or deserializing) your model instances to a some specific format. Its can be used for processing +of model instances which have taken from a database. + +Defining model serializer +^^^^^^^^^^^^^^^^^^^^^^^^^ +Let's suppose that we have a some class which has certain functionality. For example it could be a +class that storing data about a registered user: + +.. code-block:: python + + from django.db import models + + class User(models.Model): + self.username = models.CharField(max_length=255, unique=True) + self.email = models.EmailField() + self.logged_at = models.DateTimeField(auto_now=True) + +After this we will declare a serializer class which is used for serializing and deserializing +some data that converted to :class:`User` objects: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + +That's all! We have described our simplest serializer class that giving opportunities to work +with :class:`User` instances. As you can see there, we have declared :class:`Meta` class, that is +storing a link to the :class:`User` model. When we will put some data to the serializer instance +(or otherwise, update data), serializer will parse specified model and extract fields that will +be processed. + +Serializing +^^^^^^^^^^^ +Serialization mechanism let us to convert complex types into Python language types. So for it is +enough pass an existing object into a serializer instance and get ``data`` attribute after creating +serializer. For example: + +.. code-block:: python + + user = User.objects.create(username='nayton', email='nayton@example.com') + serializer = UserSerializer(user) + serializer.data + # {'pk': 1, 'username': 'nayton', 'email': 'nayton@example.com', 'logged_at': '2016-11-29T21:13:31.039488'} + +As you can see, we have converted passed object into dictionary. So it now remains to make an +additional step, that allow to transmit the data through a network. For instance we can render it +into JSON: + +.. code-block:: python + + from aiorest_ws.renderers import JSONRenderer + + json = JSONRenderer().render(serializer.data) + json + # b'{"pk": 1, "username": "nayton", "email": "nayton@example.com", "logged_at": "2016-11-29T21:13:31.039488"}' + +Deserializing +^^^^^^^^^^^^^ +Deserializing data is very useful feature when you want to get information after users action or +from 3rd party APIs and save it in a database as some model instances. For using this feature +enough to use already declared serializer class: + +.. code-block:: python + + data = {"username": "new_user", "email": "new_user@example.com", "logged_at": "2016-11-29T21:15:31.078217"} + serializer = UserSerializer(data=data) + serializer.is_valid() + # True + serializer.validated_data + # {'username': 'new_user', 'email': 'new_user@example.com', 'logged_at': datetime.datetime(2016, 11, 29, 21, 15, 31, 78217)} + +Saving instances +^^^^^^^^^^^^^^^^ +In some cases if you want to return created object instances which based on the validated data +then you will need to implement ``.create()`` and/or ``.update()`` methods. For example with +Django ORM it can be looks like: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + + def create(self, validated_data): + return User.objects.create(**validated_data) + + def update(self, instance, validated_data): + instance.username = validated_data.get('username', instance.username) + instance.email = validated_data.get('email', instance.email) + instance.logged_at = validated_data.get('logged_at', instance.logged_at) + instance.save() + return instance + +After then we implemented ``.save()`` method, it will return an object instance, based on the +validated data: + +.. code-block:: python + + user = serializer.save() + +Worth noting that instances of :class:`ModelSerializer` class can accept instance of a model as the +an argument which is can be updated further. It leads to two different calls which can be used: + +.. code-block:: python + + # The first case: `.save()` will create a new instance. + serializer = UserSerializer(data=data) + + # The second case: `.save()` will update the existing instance. + serializer = UserSerializer(user, data=data) + +Custom implementation for ``.create`` and ``.update()`` is optional. Override it only when it +necessary for your use cases. You can implement one, both or neither of them for your own model +serializer. + +Passing additional attributes to .save() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sometimes you can caught in the situation when necessary to pass additional data to the ``.save()`` +method. These additional data can be represented like timestamps, session ids or anything else that +is not part of the validated data. For solving this issue you can just include additional keyword +arguments when calling ``.save()`` method. For example: + +.. code-block:: python + + serializer.save(session_id=request.data['session_id']) + +Each additional keyword arguments will be included in the ``serializer.validated_data`` argument +when ``.create()`` or ``.update()`` are called. + +Overriding .save() directly +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In some cases you doesn't want call the ``.create()`` or ``.update()`` methods, because in some use +case you want to do some useful work instead of them. + +The clear example for it is a feedback form which is filled by a user on your website. After a +moment when you have taken the form, you are going to send a message to your administration team +from a server with some text instead of saving or updating model instance. For example it might +look like this: + +.. code-block:: python + + class FeedbackMessageSerializer(serializers.ModelSerializer): + + class Meta: + model = FeedbackMessage + + def save(self): + email = self.validated_data['email'] + subject = self.validated_data['subject'] + message = self.validated_data['message'] + send_email(from=email, subject=subject, message=message) + +Keep in mind that in this case we have a direct access to the serializer ``.validated_data`` attribute. + +Validating +^^^^^^^^^^ +When deserializing data, you always should to call ``.is_valid()`` method. This method will +validate passed data to a serializer instance and will return ``True`` value when the passed data +are correct. Otherwise will be returned ``False`` value and all occurred errors during the +validation process will be available through ``.errors`` property where each element representing +the resulting error messages. For example: + +.. code-block:: python + + data = {"username": "nayton", "email": "string", "logged_at": "2016-11-30T14:43:12.174129"} + serializer = UserSerializer(data=data) + serializer.is_valid() + # False + serializer.errors + # {'username': ['User already exists.'], 'email': ['Enter a valid e-mail address.']} + +Raising an exception on invalid data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Our ``.is_valid()`` method also can get an optional ``raise_exception`` flag that allow to raise a +``aiorest_ws.db.orm.exceptions.ValidationError`` exception when returned any validation errors. For +example: + +.. code-block:: python + + serializer.is_valid(raise_exception=True) + +Field-level validation +~~~~~~~~~~~~~~~~~~~~~~ +Sometimes you might need to make additional checks for a certain field during the validation +process. You can apply this checks by adding ``.validate_()`` to your +:class:`ModelSerializer` subclass. Each custom field-level validation method should return the +validated value or raise a :class:`ValidationError`. For example: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + from aiorest_ws.db.orm.exceptions import ValidationError + + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + + def validate_username(self, value): + if value.lower() == "admin": + raise ValidationError("Username cannot be set to 'admin'.") + return value + +.. note:: + + If ```` is declared in your serializer with parameter ``required=False`` then + this validation method will not apply if this field is not included. + +Object-level validation +~~~~~~~~~~~~~~~~~~~~~~~ +For a case when necessary to implement complex validation where are using multiple fields, you will +need to implement ``.validate()`` method in your :class:`ModelSerialize` subclass. This method takes +a single argument which is represented as a dictionary of field values. As well this method also +should return validated values or raise а :class:`ValidationError` exception. For example: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + from aiorest_ws.db.orm.exceptions import ValidationError + + class MovieSessionSerializer(serializers.Serializer): + name = serializers.CharField(max_length=50) + description = serializers.CharField(max_length=255) + start = serializers.DateTimeField() + end = serializers.DateTimeField() + + class Meta: + model = MovieSession + + def validate(self, data): + """ + Check that the start is before the end. + """ + if data['start'] > data['end']: + raise ValidationError("Start timestamp cannot be greater than end.") + return data + +Validators +~~~~~~~~~~ +For each certain field can be applied list of validators via declaring them on the field instance. +Those validators can be represented as functions or instances of some class. Each of them takes one +argument during the validation, for example: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + from aiorest_ws.db.orm.exceptions import ValidationError + from django.core.validators import MaxLengthValidator + + def empty_string_validator(value): + if not value.strip(): + raise ValidationError('Title cannot be empty.') + + class PageSerializer(serializers.ModelSerializer): + title = serializers.CharField(validators=[empty_string_validator, MaxLengthValidator(50)]) + ... + +Also you can apply similar checks to the complete set of field data. For using this mechanism will +be enough to specify ``validators`` attribute (as a list of used validators) in inner +:class:`Meta` class, like here: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + from aiorest_ws.db.orm.validators import BaseUniqueFieldValidator + + class UniqueTogetherValidator(BaseUniqueFieldValidator): + """ + Special validator class that check on uniqueness pair of fields. + """ + def __init__(self, queryset, fields, message=None): + super(UniqueTogetherValidator, self).__init__(queryset, message) + self.fields = fields + + def __call__(self, attrs): + ... + + class TrackSerializer(serializers.ModelSerializer): + name = serializers.CharField(max_length=100) + duration = serializers.TimeField() + track_number = serializers.IntegerField() + cd_name = serializers.CharField(max_length=255) + + class Meta: + model = Track + validators = [ + UniqueTogetherValidator( + queryset=Track.objects.all(), + fields=('track_number', 'cd_name') + ), + ] + +Accessing the initial data and instance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +After creating an instance of a :class:`ModelSerializer` subclass you can get access to following +attributes which can be useful during validation or creating/updating objects: ``.instance`` and +``.initial_data``. + +The first one, ``.instance`` attribute, can be set as the first argument of aserializer subclass +or with "instance" keyword. The passed instance can be initial object or queryset. If no initial +object is passed then the ``.instance`` attribute will be ``None``. + +The second one, ``.initial_data`` attribute, can be set as the second argument of a serializer +subclass or with "data" keyword. And when you are passing data to a serializer instance, the +unmodified data will be made available as ``.initial_data``. If the ``data`` keyword argument is +not passed then the ``.initial_data`` attribute will not exist. + +Partial instance updates +^^^^^^^^^^^^^^^^^^^^^^^^ +By default for each serializer you must specify all required fields or it will raise validation +errors. For using partial updates you will need to pass ``partial`` flag with ``True`` value. For +example: + +.. code-block:: python + + # Update `user` with partial data + serializer = UserSerializer(user, data={'email': 'new_nayton_email@exampl.com'}, partial=True) + +Dealing with nested objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The above examples are pretty fine demonstrating how to work with a models that have a simple +datatypes. But in most projects with which you will be work, will be have models this relations +with which also necessary to work. And you expecting that will be able to represent more complex +objects, that contains not only default datatypes. + +Because each field class and :class:`ModelSerializer` subclass have the same parent +:class:`AbstractSerializer` class, you can use these model serializers for represent relationships +where one object type is nested inside another. + +.. note:: + And here we have a difference with Django REST framework. The author of aiorest-ws library have + divided the :class:`Field` class onto two different classes: model serializer and its model + fields. This is done in order to make it easier for programmers to understand what is happening + in the process of debugging. + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + + class CategorySerializer(serializers.ModelSerializer): + name = serializers.CharField(max_length=255) + + class Meta: + model = Category + + class PostSerializer(serializers.Serializer): + category = CategorySerializer() + title = serializers.CharField(max_length=255) + content = serializers.CharField(max_length=3000) + + class Meta: + model = Post + +In the case if a nested serializer may accept the ``None`` value, you should pass the +``required=False`` value to this nested serializer: + +.. code-block:: python + + class PostSerializer(serializers.Serializer): + category = CategorySerializer(required=False) # Post can be without a category + title = serializers.CharField(max_length=255) + content = serializers.CharField(max_length=3000) + + class Meta: + model = Post + +For a case when a nested serializer should be represented as a list of objects, specify the +``many=True`` value to the nested serializer: + +.. code-block:: python + + class PostSerializer(serializers.Serializer): + category = CategorySerializer(many=True) # A list of categories + title = serializers.CharField(max_length=255) + content = serializers.CharField(max_length=3000) + + class Meta: + model = Post + +Writable nested representations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When dealing with nested representations that support deserializing the data, any errors with +nested objects will be nested under the field name of the nested object. + +.. code-block:: python + + data = {'category': {'name': ''}, 'title': 'aiorest-ws docs', 'content': 'The first version of docs.'} + serializer = PostSerializer(data=data) + serializer.is_valid() + # False + serializer.errors + # {'category': {'name': ['This field may not be blank.']}} + +Similarly, the ``.validated_data`` property will include nested data structures. + +Writing .create() methods for nested representations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For a case when necessary to support writable nested representations for a model serializer, you +will need to override ``.create()`` or ``.update()`` methods that giving opportunities to work with +multiple objects. + +Take a look on the next example that demonstrate you how to create a :class:`Post` instance with +nested objects: + +.. code-block:: python + + from aiorest_ws.db.orm.django import serializers + + class PostSerializer(serializers.Serializer): + category = CategorySerializer(many=True) + title = serializers.CharField(max_length=255) + content = serializers.CharField(max_length=3000) + + class Meta: + model = Post + fields = ('category', 'title', 'content') + + def create(self, validated_data): + category_data = validated_data.pop('category') + post = Post.objects.create(**validated_data) + Category.objects.create(post=post, **category_data) + return post + +Writing .update() methods for nested representations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For a case when necessary to update an instance with nested relationships is more complex. First of +all you must decide how to handle updates to relationships. What to do if the data for model +relationship is ``None`` value, or perhaps not provided? We can use one of the following solutions: + +- Set the relationship to NULL in the database. +- Delete the associated instance. +- Ignore the data and leave the instance as it is. +- Raise a validation error. + +Take a look on the next example for an ``.update()`` method of :class:`PostSerializer` class: + +.. code-block:: python + + def update(self, instance, validated_data): + category_data = validated_data.pop('category') + # Unless the application properly enforces that this field is + # always set, the follow could raise a `DoesNotExist`, which + # would need to be handled. + category_instance = instance.category + category_instance.name = category_data.get('name', 'Blog') + category_instance.save() + + instance.title = validated_data.get('title', instance.title) + instance.content = validated_data.get('content', instance.content) + instance.save() + + return instance + +Because the behavior of nested creates and updates can be ambiguous, and may require complex +dependencies between related models, :class:`ModelSerializer` requires you to always write these +methods explicitly. The default :class:`ModelSerializer` ``.create()`` and ``.update()`` methods do +not include support for writable nested representations. + +Dealing with multiple objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +For each supported ORM the :class:`ModelSerializer` class can also handle serializing or +deserializing lists of objects. + +Serializing multiple objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For a case when necessary to serialize a queryset or list of objects instead of a single object +instance, just specify the ``many=True`` flag when instantiating the model serializer. You can then +specify a queryset or list of objects to be serialized. + +.. code-block:: python + + queryset = Category.objects.all() + serializer = CategorySerializer(queryset, many=True) + serializer.data + # [ + # {'name': 'Documentation'}, + # {'name': 'Features'}, + # {'name': 'Change notes'} + # ] + +Deserializing multiple objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The default behavior for deserializing multiple objects is to support multiple object creation, +but not support multiple object updates. For more information on how to support or customize either +of these cases, see the ListSerializer documentation below. + +Including extra context +^^^^^^^^^^^^^^^^^^^^^^^ +In some cases you may need to provide extra context which can be used by serializer during the +validating or in addition to the object being serialized. You can do this by passing a ``context`` +argument when instantiating the serializer. For example: + +.. code-block:: python + + + from aiorest_ws.db.orm.django import serializers + + class UserSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ('username', 'email') + + def to_representation(self, instance): + data = super(UserSerializer, self).to_representation(instance) + data['request_id'] = self.context['request'].request_id + return data + + serializer = UserSerializer(user, context={'request': request}) + serializer.data + # {"username": "nayton", "email": "nayton@example.com", "request_id": "76c3d654-b804-11e6-a794-0c4de9c846b0"} + +The context dictionary can be used within any serializer field logic, such as a custom +``.to_representation()`` method, by accessing the ``self.context`` attribute. + +ModelSerializer +^^^^^^^^^^^^^^^ + +HyperlinkedModelSerializer +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ListSerializer +^^^^^^^^^^^^^^ + +Serializer fields +----------------- + +BigIntegerField +^^^^^^^^^^^^^^^ + +BooleanField +^^^^^^^^^^^^ + +CharField +^^^^^^^^^ + +ChoiceField +^^^^^^^^^^^ + +CreateOnlyDefault +^^^^^^^^^^^^^^^^^ + +DateField +^^^^^^^^^ + +DateTimeField +^^^^^^^^^^^^^ + +DecimalField +^^^^^^^^^^^^ + +DictField +^^^^^^^^^ + +DurationField +^^^^^^^^^^^^^ + +EmailField +^^^^^^^^^^ + +EnumField +^^^^^^^^^ + +FileField +^^^^^^^^^ + +FilePathField +^^^^^^^^^^^^^ + +FloatField +^^^^^^^^^^ + +HStoreField +^^^^^^^^^^^ + +IPAddressField +^^^^^^^^^^^^^^ + +ImageField +^^^^^^^^^^ + +IntegerField +^^^^^^^^^^^^ + +IntervalField +^^^^^^^^^^^^^ + +JSONField +^^^^^^^^^ + +LargeBinaryField +^^^^^^^^^^^^^^^^ + +ListField +^^^^^^^^^ + +ModelField +^^^^^^^^^^ + +MultipleChoiceField +^^^^^^^^^^^^^^^^^^^ + +NullBooleanField +^^^^^^^^^^^^^^^^ + +PickleField +^^^^^^^^^^^ + +ReadOnlyField +^^^^^^^^^^^^^ + +RegexField +^^^^^^^^^^ + +SerializerMethodField +^^^^^^^^^^^^^^^^^^^^^ + +SlugField +^^^^^^^^^ + +SmallIntegerField +^^^^^^^^^^^^^^^^^ + +TimeField +^^^^^^^^^ + +URLField +^^^^^^^^ + +UUIDField +^^^^^^^^^ + +Serializer relation fields +-------------------------- + +PrimaryKeyRelatedField +^^^^^^^^^^^^^^^^^^^^^^ + +HyperlinkedRelatedField +^^^^^^^^^^^^^^^^^^^^^^^ + +HyperlinkedIdentityField +^^^^^^^^^^^^^^^^^^^^^^^^ + +StringRelatedField +^^^^^^^^^^^^^^^^^^ + +SlugRelatedField +^^^^^^^^^^^^^^^^ diff --git a/docs/source/wrappers.rst b/docs/source/wrappers.rst index b3deca7..4f418b6 100644 --- a/docs/source/wrappers.rst +++ b/docs/source/wrappers.rst @@ -26,21 +26,21 @@ Properties: Returns dictionary of arguments for request. - For instance in the "classical REST" you can send the HTTP request by certain URL - like http://mywebsite.com/api/user/?name=admin&password=mysecretpassword where with ``?`` separator - and ``&`` symbols you can specify all required arguments where it necessary. In our case for - aiorest-ws library you should specify all this arguments in request as a dictionary. For example: - -.. code-block:: javascript - - { - "method": "GET", - "url": "/api/user, - "args": { - "name": "admin", - "password": "mysecretpassword" + For instance in the "classical REST" you can send the HTTP request by certain URL like + http://mywebsite.com/api/user/?name=admin&password=mysecretpassword where with ``?`` and ``&`` + symbols you can specify all required arguments when it is necessary. For the following example + we specify all the same arguments in request at the dictionary: + + .. code-block:: python + + { + "method": "GET", + "url": "/api/user", + "args": { + "name": "admin", + "password": "mysecretpassword" + } } - } - data