Change data in ModelViewSet
Project description
Restframework-datachange is an enhancement of rest_framework. To use it, we must have basic understandings of Django and rest_framework.
1. Installation
As usual, use pip:
$ pip install restframework_datachange
$ pip install django
$ pip install djangorestframework
2. Before using restframework_datachange
We make a sample restframework-support project.
$ django-admin startproject my_project
my_project$ cd my_project
my_project$ django-admin startapp movie
Now we write files as followed:
Make models.
my_project/movie/models.py
from django.db import models
# Create your models here.
class Cast(models.Model):
SEX_CHOICE = ((1, 'Male'), (2, 'Female'))
sex = models.IntegerField(choices=SEX_CHOICE, default=1)
profession = models.CharField(max_length=100, default='')
foreign_name = models.CharField(max_length=100, default='')
def __str__(self):
return self.foreign_name
class OneMovie(models.Model):
COUNTRY_CHOICE = ((1, 'USA'), (2, 'UK'))
name = models.CharField(max_length=200, default='')
director = models.ManyToManyField(Cast, related_name='director')
actors = models.ManyToManyField(Cast, related_name='actors')
country = models.IntegerField(default='', choices=COUNTRY_CHOICE)
def __str__(self):
return self.name
Make Serializers. For usage of SlugRelatedField
, please turn to documents of djangorestframework
my_project/movie/serializers.py
from rest_framework.serializers import ModelSerializer, SlugRelatedField
from movie.models import OneMovie, Cast
class MovieSerializer(ModelSerializer):
director = SlugRelatedField(slug_field='foreign_name', queryset=Cast.objects.all(), many=True)
actors = SlugRelatedField(slug_field='foreign_name', queryset=Cast.objects.all(), many=True)
class Meta:
model = OneMovie
fields = '__all__'
Make views
my_project/movie/views.py
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from movie.models import OneMovie
from movie.serializers import MovieSerializer
# Create your views here.
class MovieViewSet(ModelViewSet):
queryset = OneMovie.objects.all()
serializer_class = MovieSerializer
Change settings
my_project/my_project/settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'movie'
]
...
Make migrations and migrate.
my_project/my_project $ cd ..
my_project $ python manage.py makemigrations
Migrations for 'movie':
movie/migrations/0001_initial.py
- Create model Cast
- Create model OneMovie
my_project$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, movie, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying movie.0001_initial... OK
Applying sessions.0001_initial... OK
Make URLs for the whole project.
my_project/my_project/urls.py
from django.contrib import admin
from django.urls import path
from rest_framework import routers
from movie.views import MovieViewSet
urlpatterns = [
path('admin/', admin.site.urls),
]
router = routers.DefaultRouter()
router.register(r'movie', MovieViewSet, base_name='movie')
urlpatterns += router.urls
Now we create some data for the database.
my_project $ python manage.py shell
In [1]: from movie.models import OneMovie
In [2]: from movie.models import Cast
In [3]: harry = Cast.objects.create(sex=1, profession='actor', foreign_name='harry')
Out[3]: <Cast: harry>
In [4]: harry
Out[4]: <Cast: harry>
In [5]: harry_potter = OneMovie.objects.create(name='Harry Potter', country=1)
In [6]: harry_potter.director.add(harry)
In [7]: harry_potter.actors.add(harry)
In [8]: harry_potter.save()
In [9]: harry_potter
Out[9]: <OneMovie: Harry Potter>
In [10]: harry_potter.director.all()
Out[10]: <QuerySet [<Cast: harry>]>
In [11]: harry_potter.actors.all()
Out[11]: <QuerySet [<Cast: harry>]>
Now we run the server.
my_project/my_project $ cd ..
my_project $ python manage.py runserver 0.0.0.0:8000
Click "movie": "http://127.0.0.1:8000/movie/"
Ta da! Everything seems perfect. Below is what you'll see.
[
{
"id": 1,
"director": [
"harry"
],
"actors": [
"harry"
],
"name": "Harry Potter",
"country": 1
}
]
3. Changing Data
We just want the name to be 'Harry' instead of 'Harry Potter'? That is to say, to change the data returned? That's when restframework_datachange comes in!
my_project/movie/views.py
from restframework_datachange.viewsets import RModelViewSet # changed
from movie.models import OneMovie
from movie.serializers import MovieSerializer
# Create your views here.
class MovieAdjust(object): # changed
def change_name(self, value): # changed
return value.split(' ')[0] # changed
class MovieViewSet(MovieAdjust, RModelViewSet): # changed
queryset = OneMovie.objects.all()
serializer_class = MovieSerializer
By making an Adjust
object and changing inheritance to RModelViewSet
, we can change 'Harry Potter' to 'Harry'!
[
{
"id": 1,
"director": [
"harry"
],
"actors": [
"harry"
],
"name": "Harry",
"country": 1
}
]
Now we change country code into its real name:
my_project/movie/views.py
...
class MovieAdjust(object):
def change_name(self, value):
return value.split(' ')[0]
def change_country(self, value):
dic = {1: 'UK', 2: 'US'}
return dic[value]
...
Ta da!
[
{
"id": 1,
"director": [
"harry"
],
"actors": [
"harry"
],
"name": "Harry",
"country": "UK"
}
]
4. Adding new data based on one field
We want a new field that is based on the data's original field. For example, we want a string version of actors
my_project/movie/views.py
...
class MovieAdjust(object):
string_actors_src1 = 'actors'
def change_name(self, value):
return value.split(' ')[0]
def change_country(self, value):
dic = {1: 'UK', 2: 'US'}
return dic[value]
def add_string_actors(self, value):
return ', '.join(value)[:3]
...
Make an add_xx
method, pass a value
and modify it, and specify the source field as xx_src1
. Then you can see this.
[
{
"id": 1,
"director": [
"harry"
],
"actors": [
"harry"
],
"name": "Harry",
"country": "UK",
"string_actors": "har"
}
]
4. Adding new data based on two or more fields
Suppose we want to join the country
field and the name
field to form a detail
field.
class MovieAdjust(object):
string_actors_src1 = 'actors'
detail_src1 = 'country' # xx = detail
detail_src2 = 'name' # xx = detail
def change_name(self, value):
return value.split(' ')[0]
def change_country(self, value):
dic = {1: 'UK', 2: 'US'}
return dic[value]
def add_string_actors(self, value):
return ', '.join(value)[:3]
def add_detail(self, *value): # xx = detail
return ', '.join([str(i) for i in value])
Now you can see:
[
{
"id": 1,
"director": [
"harry"
],
"actors": [
"harry"
],
"name": "Harry",
"country": "UK",
"detail": "1,Harry Potter",
"string_actors": "har"
}
]
Pay attention to the country
part in detail
, it is the original value 1
instead of the modified UK
. Here, detail_src1
goes to country
, so value[0]
is the value of country
field, 1
. value[1]
is the value of name
.
You can also write the code like this:
class MovieAdjust(object):
string_actors_src1 = 'actors'
detail_src1 = 'country'
detail_src2 = 'name'
def change_name(self, value):
return value.split(' ')[0]
def change_country(self, value):
dic = {1: 'UK', 2: 'US'}
return dic[value]
def add_string_actors(self, value):
return ', '.join(value)[:3]
def add_detail(self, country, name):
return str(country) + ', ' + str(name)
Just make sure the number and the position of parameters beside self
are the same as the src
s of your newly-named field.
5. Meddle with other actions.
If you happen to know the retrieve
, create
, update
, delete
actions of restframework, you can meddle your return by creating Adjust
objects based on the tables below:
Actions | Modification | Method Prefix | Source Field Suffix |
---|---|---|---|
list | add a field | add | src |
list | change a field | change | -- |
retrieve | add a field | append | org |
retrieve | change a field | modify | -- |
create | add a field | attach | bir |
create | change a field | reform | -- |
update | add a field | adjoin | lch |
update | change a field | vary | -- |
6. Show/hide a field
If you want to show or hide a field, you can modify the action-specific _fields
and _exclude
property.
class MovieAdjust(object):
list_exclude = ['actors']
will get you:
[
{
"id": 1,
"director": [
"harry"
],
"name": "Harry Potter",
"country": 1
}
]
class MovieAdjust(object):
list_fields = ['actors', 'name']
will get you:
[
{
"actors": [
"harry"
],
"name": "Harry Potter"
}
]
7. Turn json into a model
If you have a python dic and wants to turn it into a Django model, use modelmaker
model_maker(dic, file='fake_model.py', class_name='Default', name_changer=camel_to_, **config)
dic
A python dic.
file
The model will be written in the file specified. Passed as '' will only return a printed version.
class_name
Model name.
name_changer
A method to change some string into other string. Default is a function that turns camel cases to underlines.
config
Set the properties of model.
Example:
from restframework_datachange.model_maker import model_maker
from datetime import datetime
dic = {
'apple': 1, # IntegerField
'boy': 1.2, # FloatField
'cat': 'string', # CharField
'dog': [{'json': 1}], # JSONField
'elephant': datetime.now(), # DatetimeField
'changed': 'string'
}
config = {
'apple__choices': [(1, 'UK'), (2, 'US')],
'boy__null': True,
'cat__max_length': 20,
'dog__verbose_name': 'Dog',
'elephant__auto_now': True
}
def name_changer(string):
li = ['apple', 'boy', 'cat', 'dog', 'elephant']
if string not in li:
return 'fog'
return string
print(model_maker(dic, file='', class_name='M', name_changer=name_changer, **config))
will give you:
from django.contrib.postgres.fields import JSONField
from django.db import models
class M(models.Model):
apple = models.IntegerField(verbose_name="", help_text="", null=True, choices=[(1, 'UK'), (2, 'US')])
boy = models.FloatField(verbose_name="", help_text="", null=True)
cat = models.CharField(verbose_name="", help_text="", default="", max_length=20)
dog = JSONField(verbose_name="Dog", help_text="", null=True, blank=True)
elephant = models.DateTimeField(verbose_name="", help_text="", auto_now=True, auto_now_add=False)
fog = models.CharField(verbose_name="", help_text="", default="", max_length=64)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for restframework_datachange-0.0.1.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | f21edbda876c825af9384f57342021cae5446926acaef52fb8ac7a8bff00429e |
|
MD5 | 28f058160ed32ebb2ac4fab1e0e45eb3 |
|
BLAKE2b-256 | b0720bd65800ac9fbbdcbccd3ac12bbed6d65d20a83ca1f9483e616dbeae3d57 |
Hashes for restframework_datachange-0.0.1-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 75d2d9b3dbf12cbf94d420377876399547d6e50bdac62de9d66e6ec153922604 |
|
MD5 | d8b2305ee790e05f50b87c0b40d4c534 |
|
BLAKE2b-256 | 1aca399ff253de77184d0217c0a6bae2282a9c4af12456e4a50fc9c1e2a9469f |