{ "info": { "author": "Ron Ding", "author_email": "ronweasleyding@163.com", "bugtrack_url": null, "classifiers": [], "description": "Restframework-datachange is an enhancement of rest_framework. To use it, we must have basic understandings of Django and rest_framework. \n\n# 1. Installation\n\nAs usual, use pip:\n\n```\n$ pip install restframework_datachange\n$ pip install django\n$ pip install djangorestframework\n```\n\n# 2. Before using restframework_datachange\n\nWe make a sample restframework-support project.\n\n\n```\n$ django-admin startproject my_project\nmy_project$ cd my_project\nmy_project$ django-admin startapp movie\n```\nNow we write the files as followed:\n\n## Make models.\n\nmy_project/movie/models.py\n```python\nfrom django.db import models\n\nclass Cast(models.Model):\n SEX_CHOICE = ((1, \"Male\"), (2, \"Female\"))\n\n sex = models.IntegerField(choices=SEX_CHOICE, default=1)\n profession = models.CharField(max_length=100, default=\"\")\n foreign_name = models.CharField(max_length=100, default=\"\")\n\n\n def __str__(self):\n return self.foreign_name\n\n\nclass OneMovie(models.Model):\n COUNTRY_CHOICE = ((1, \"UK\"), (2, \"US\"))\n\n name = models.CharField(max_length=200, default=\"\")\n director = models.ManyToManyField(Cast, related_name=\"director\")\n actors = models.ManyToManyField(Cast, related_name=\"actors\")\n country = models.IntegerField(default=\"\", choices=COUNTRY_CHOICE)\n\n\n def __str__(self):\n return self.name\n\n```\n\n## Make Serializers. For usage of ```SlugRelatedField```, please refer to [the document of djangorestframework](https://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield).\n\nmy_project/movie/serializers.py\n```python\nfrom rest_framework.serializers import ModelSerializer, SlugRelatedField\nfrom movie.models import OneMovie, Cast\n\n\nclass MovieSerializer(ModelSerializer):\n director = SlugRelatedField(slug_field=\"foreign_name\", queryset=Cast.objects.all(), many=True)\n actors = SlugRelatedField(slug_field=\"foreign_name\", queryset=Cast.objects.all(), many=True)\n class Meta:\n model = OneMovie\n fields = \"__all__\"\n```\n\n## Make views\n\nmy_project/movie/views.py\n```python\nfrom django.shortcuts import render\nfrom rest_framework.viewsets import ModelViewSet\nfrom movie.models import OneMovie\nfrom movie.serializers import MovieSerializer\n\n\nclass MovieViewSet(ModelViewSet):\n queryset = OneMovie.objects.all()\n serializer_class = MovieSerializer\n\n```\n\n## Change settings\nmy_project/my_project/settings.py\n\n```python\n...\n\nINSTALLED_APPS = [\n \"django.contrib.admin\",\n \"django.contrib.auth\",\n \"django.contrib.contenttypes\",\n \"django.contrib.sessions\",\n \"django.contrib.messages\",\n \"django.contrib.staticfiles\",\n \"rest_framework\", #\n \"movie\" #\n\n]\n\n...\n\n```\n## Make migrations and migrate.\n\n```\nmy_project/my_project $ cd ..\nmy_project $ python manage.py makemigrations\nMigrations for 'movie':\n movie/migrations/0001_initial.py\n - Create model Cast\n - Create model OneMovie\n\nmy_project$ python3 manage.py migrate\nOperations to perform:\n Apply all migrations: admin, auth, contenttypes, movie, sessions\nRunning migrations:\n Applying contenttypes.0001_initial... OK\n Applying auth.0001_initial... OK\n Applying admin.0001_initial... OK\n Applying admin.0002_logentry_remove_auto_add... OK\n Applying admin.0003_logentry_add_action_flag_choices... OK\n Applying contenttypes.0002_remove_content_type_name... OK\n Applying auth.0002_alter_permission_name_max_length... OK\n Applying auth.0003_alter_user_email_max_length... OK\n Applying auth.0004_alter_user_username_opts... OK\n Applying auth.0005_alter_user_last_login_null... OK\n Applying auth.0006_require_contenttypes_0002... OK\n Applying auth.0007_alter_validators_add_error_messages... OK\n Applying auth.0008_alter_user_username_max_length... OK\n Applying auth.0009_alter_user_last_name_max_length... OK\n Applying movie.0001_initial... OK\n Applying sessions.0001_initial... OK\n```\n\n## Make URLs for the whole project.\n\nmy_project/my_project/urls.py\n```python\nfrom django.contrib import admin\nfrom django.urls import path\nfrom rest_framework import routers\nfrom movie.views import MovieViewSet\n\nurlpatterns = [\n path(\"admin/\", admin.site.urls),\n]\n\nrouter = routers.DefaultRouter()\nrouter.register(r\"movie\", MovieViewSet, base_name=\"movie\")\n\n\nurlpatterns += router.urls\n```\n\n## Now we create some data for the database.\n\n```python\nmy_project $ python manage.py shell\n\nIn [1]: from movie.models import OneMovie\n\nIn [2]: from movie.models import Cast\n\nIn [3]: daniel = Cast.objects.create(sex=1, profession=\"actor\", foreign_name=\"Daniel\")\n\nIn [4]: daniel\nOut[4]: \n\nIn [5]: emma = Cast.objects.create(sex=2, profession=\"actor\", foreign_name=\"Emma\")\n\nIn [6]: Cast.objects.all()\nOut[6]: , ]>\n\nIn [7]: david = Cast.objects.create(sex=1, profession=\"director\", foreign_name=\"David\")\n\nIn [8]: harry_potter_movie = OneMovie.objects.create(name=\"Harry Potter and the Goblet of Fire\", country=2)\n\nIn [9]: harry_potter_movie.director.add(david)\n\nIn [10]: harry_potter_movie.actors.add(daniel)\n\nIn [11]: harry_potter_movie.actors.add(emma)\n\nIn [12]: harry_potter_movie.save()\n\nIn [13]: harry_potter_movie\nOut[13]: \n\nIn [14]: harry_potter_movie.director.all()\nOut[14]: ]>\n\nIn [15]: harry_potter_movie.actors.all()\nOut[16]: , ]>\n\nIn [16]: exit()\n```\n\n\n## Now we run the server.\n```\nmy_project $ python manage.py runserver 0.0.0.0:8000\n\n```\n\n## Start your browser and enter ```http://127.0.0.1:8000/```, click ```\"movie\": \"http://127.0.0.1:8000/movie/\"```\n\nTa da! Everything seems perfect. Below is what you'll see.\n```\n[\n {\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry Potter and the Goblet of Fire\",\n \"country\": 2\n }\n]\n```\n\n# 3. Changing Data \n\nWe just want the movie ```\"name\"``` to be ```\"Harry\"``` instead of ```\"Harry Potter and the Goblet of File\"```. That is to say, we want to change the data returned. That's when restframework_datachange comes in!\n\nmy_project/movie/views.py\n```python\nfrom restframework_datachange.viewsets import RModelViewSet # \nfrom movie.models import OneMovie\nfrom movie.serializers import MovieSerializer\n\n\nclass MovieAdjust(object): # \n def change_name(self, value): # xx = name\n return value.split(\" \")[0] # \n\nclass MovieViewSet(MovieAdjust, RModelViewSet): # \n queryset = OneMovie.objects.all()\n serializer_class = MovieSerializer\n```\n\nBy adding a ```change_xx``` to the ```Adjust``` object and changing inheritance to ```RModelViewSet```, we change the value of the returned ```\"name\"``` field from ```\"Harry Potter and the Goblet of File\"``` to ```\"Harry\"```!\n\n\n```\n[\n {\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry\",\n \"country\": 2\n }\n]\n\n```\n\nNow we change the ```\"country\"``` code into correspondent country name:\n\nmy_project/movie/views.py\n```python\n...\nclass MovieAdjust(object): \n def change_name(self, value): \n return value.split(\" \")[0] \n\n def change_country(self, value):\n dic = {1: \"UK\", 2: \"US\"}\n return dic[value]\n...\n```\n\nTa da!\n\n```\n[\n {\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry\",\n \"country\": \"US\" \n }\n]\n```\n\n# 4. Adding new data based on one field\n\nWe want a new field that is based on the data's original field. For example, we want a string version of ```\"actors\"``` named as ```\"string_actors\"```.\n\n\nmy_project/movie/views.py\n```python\n...\nclass MovieAdjust(object):\n string_actors_src1 = \"actors\"\n\n def change_name(self, value):\n return value.split(\" \")[0]\n\n def change_country(self, value):\n dic = {1: \"UK\", 2: \"US\"}\n return dic[value]\n\n def add_string_actors(self, value):\n return \", \".join(value)\n...\n```\n\nMake an ```add_xx``` method, pass a ```value``` and modify it, and specify the source field as ```xx_src1```. Then you can see this.\n\n```\n[\n {\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry\",\n \"country\": \"US\",\n \"string_actors\": \"Daniel, Emma\"\n }\n]\n```\n\n# 4. Adding new data based on two or more fields\n\nSuppose we want to join the ```\"country\"``` field and the ```\"name\"``` field to form a ```\"detail\"``` field.\n\n```python\nclass MovieAdjust(object):\n string_actors_src1 = \"actors\"\n detail_src1 = \"country\" # xx = detail\n detail_src2 = \"name\" # xx = detail\n\n def change_name(self, value):\n return value.split(\" \")[0]\n\n def change_country(self, value):\n dic = {1: \"UK\", 2: \"US\"}\n return dic[value]\n\n def add_string_actors(self, value):\n return \", \".join(value)\n\n def add_detail(self, *value): # xx = detail\n return \", \".join([str(i) for i in value])\n```\n\nNow you can see:\n\n```\n[\n {\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry\",\n \"country\": \"US\",\n \"detail\": \"2, Harry Potter and the Goblet of Fire\",\n \"string_actors\": \"Daniel, Emma\"\n }\n]\n```\n\nPay attention to the ```\"country\"``` part in ```\"detail\"```, it is the original value ```2``` instead of the modified value ```\"US\"```. Here, ```\"detail\"``` has source fields ```\"country\"``` and ```\"name\"```, so ```value[0]``` is the value of ```\"country\"``` field, ```2```. And ```value[1]``` is the value of ```\"name\"```, ```\"Harry Potter and the Goblet of Fire\"```.\n\nYou can also write the code like this:\n\n```python\nclass MovieAdjust(object):\n string_actors_src1 = \"actors\"\n detail_src1 = \"country\"\n detail_src2 = \"name\"\n\n def change_name(self, value):\n return value.split(\" \")[0]\n\n def change_country(self, value):\n dic = {1: \"UK\", 2: \"US\"}\n return dic[value]\n\n def add_string_actors(self, value):\n return \", \".join(value)\n\n def add_detail(self, country, name): \n return str(country) + \", \" + str(name)\n```\n\nMake sure the number and the position of parameters beside ```self``` are the same as the ```src```s of your newly-created field.\n\n# 5. Meddle with other actions.\n\nIf you happen to know the ```retrieve```, ```create```, ```update```, actions of restframework, you can meddle your return by creating ```Adjust``` objects based on the tables below: \n\n| Actions | Modification | Method Prefix | Source Field Suffix |\n|:---------|:--------------:|:-------------:|:-------------------:|\n| list | add a field | add | src |\n| list | change a field | change | -- |\n| retrieve | add a field | append | org |\n| retrieve | change a field | modify | -- |\n| create | add a field | attach | bir |\n| create | change a field | reform | -- |\n| update | add a field | adjoin | lch |\n| update | change a field | vary | -- |\n\nFor example:\n\nhttp://127.0.0.1:8000/movie/1/ calls the ```retrieve``` method.\n\n```python\nclass MovieAdjust(object):\n string_actors_src1 = \"actors\"\n detail_src1 = \"country\" # xx = detail(list)\n detail_src2 = \"name\" # xx = detail(list)\n string_actors_retrieve_org1 = \"actors\" # yy = string_actors_retrieve(retrieve)\n\n def change_name(self, value):\n return value.split(\" \")[0]\n\n def change_country(self, value):\n dic = {1: \"UK\", 2: \"US\"}\n return dic[value]\n\n def add_string_actors(self, value):\n return \", \".join(value)\n\n def add_detail(self, *value): # xx = detail\n return \", \".join([str(i) for i in value])\n\n def modify_country(self, value):\n dic = {1: \"United Kingdom\", 2: \"United States\"}\n return dic[value]\n\n def append_string_actors_retrieve(self, value): \n # yy = string_actors_retrieve\n return \", \".join(value)\n```\n\n\n```\n{\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry Potter and the Goblet of Fire\",\n \"country\": \"United States\",\n \"string_actors_retrieve\": \"Daniel, Emma\"\n}\n\n```\n\n\n# 6. Show/hide a field\n\nIf you want to show or hide a field, you can modify the action-specific ```_fields``` and ```_exclude``` property.\n\n```python\nclass MovieAdjust(object):\n list_exclude = [\"actors\"]\n\n```\n\nwill get you:\n\n```\n[\n {\n \"id\": 1,\n \"director\": [\n \"David\"\n ],\n \"name\": \"Harry Potter and the Goblet of Fire\",\n \"country\": 2\n }\n]\n```\n\n```python\nclass MovieAdjust(object):\n list_fields = [\"actors\", \"name\"]\n\n```\n\nwill get you:\n\n```\n[\n {\n \"actors\": [\n \"Daniel\",\n \"Emma\"\n ],\n \"name\": \"Harry Potter and the Goblet of Fire\"\n }\n]\n\n```\n\n# 7. Turn python dict into a model\n\nIf you have a python dict and wants to turn it into a Django model, you can use ```modelmaker```.\n```python\nmodel_maker(dic, file='fake_model.py', class_name='Default', name_changer=camel_to_, default_settings=None, **config)\n```\n\n\n```dic``` : A python dic.\n\n```file``` : The model will be written in the file specified. Passed as ```\"\"``` and it will only return a printed version.\n\n```class_name``` : Model name.\n\n```name_changer``` : A method to change some string into another string. Default is a function that turns camel cases to underlines.\n\n```default_settings```: A python dict to specify default properties settings of a type of field.\n\n```config``` : Set the properties of model. For detail, please refer to [the documents of django](https://docs.djangoproject.com/en/2.2/ref/models/fields/).\n\nExample:\n\n```python\n\nfrom restframework_datachange.model_maker import model_maker\nfrom datetime import datetime\n\ndic = {\n \"apple\": 1, # IntegerField\n \"boy\": 1.2, # FloatField\n \"cat\": \"string\", # CharField\n \"dog\": [{\"json\": 1}], # JSONField\n \"elephant\": datetime.now(), # DatetimeField\n \"changed\": \"string\"\n}\n\nconfig = {\n \"apple__choices\": [(1, \"UK\"), (2, \"US\")],\n \"boy__null\": True,\n \"cat__max_length\": 20,\n \"dog__verbose_name\": \"Dog\",\n \"elephant__auto_now\": True\n}\n\ndef name_changer(string):\n li = [\"apple\", \"boy\", \"cat\", \"dog\", \"elephant\"]\n if string not in li:\n return \"fog\"\n return string\n\nprint(model_maker(dic, file=\"\", class_name=\"M\", name_changer=name_changer, **config))\n\n\n```\n\nwill give you:\n\n```python\nfrom django.contrib.postgres.fields import JSONField\nfrom django.db import models\n\n\nclass M(models.Model):\n apple = models.IntegerField(verbose_name=\"\", help_text=\"\", null=True, choices=[(1, \"UK\"), (2, \"US\")])\n boy = models.FloatField(verbose_name=\"\", help_text=\"\", null=True)\n cat = models.CharField(verbose_name=\"\", help_text=\"\", default=\"\", max_length=20)\n dog = JSONField(verbose_name=\"Dog\", help_text=\"\", null=True, blank=True)\n elephant = models.DateTimeField(verbose_name=\"\", help_text=\"\", auto_now=True, auto_now_add=False)\n fog = models.CharField(verbose_name=\"\", help_text=\"\", default=\"\", max_length=64)\n\n\n```\n\nYou can change the default settings of ```model_maker``` by passing a python dict to the ```default_settings``` parameter.\n\n```python\ndic = {\"apple\": 1} # IntegerField\n\nprint(model_maker(dic, file=\"\", class_name=\"AppleCart\", default_settings={\"int\": {\"null\":True}}))\n```\n\nwill give you:\n```python\nfrom django.db import models\n\n\nclass AppleCart(models.Model):\n apple = models.IntegerField(null=True)\n```\n\nDefault settings for different types are as followed:\n\n```python\n{\n \"int\": {\"verbose_name\": \"\", \"help_text\": \"\", \"null\": True},\n \"str\": {\"verbose_name\": \"\", \"help_text\": \"\", \"default\": \"\", \"max_length\": 64},\n \"datetime\": {\"verbose_name\": \"\", \"help_text\": \"\", \"auto_now\": False, \"auto_now_add\": False},\n \"date\": {\"verbose_name\": \"\", \"help_text\": \"\", \"auto_now\": False, \"auto_now_add\": False},\n \"json\": {\"verbose_name\": \"\", \"help_text\": \"\", \"null\": True, \"blank\": True},\n \"bool\": {\"verbose_name\": \"\", \"help_text\": \"\", \"null\": True},\n \"float\": {\"verbose_name\": \"\", \"help_text\": \"\", \"null\": True}\n}\n```\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/RonDingDing/restframework_datachange", "keywords": "", "license": "Anti 996 License", "maintainer": "", "maintainer_email": "", "name": "restframework-datachange", "package_url": "https://pypi.org/project/restframework-datachange/", "platform": "", "project_url": "https://pypi.org/project/restframework-datachange/", "project_urls": { "Homepage": "https://github.com/RonDingDing/restframework_datachange" }, "release_url": "https://pypi.org/project/restframework-datachange/0.0.2.1/", "requires_dist": [ "django (>=2.1.7)", "djangorestframework (>=3.9.1)" ], "requires_python": "", "summary": "Change data in ModelViewSet", "version": "0.0.2.1" }, "last_serial": 5581567, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "d8b2305ee790e05f50b87c0b40d4c534", "sha256": "75d2d9b3dbf12cbf94d420377876399547d6e50bdac62de9d66e6ec153922604" }, "downloads": -1, "filename": "restframework_datachange-0.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d8b2305ee790e05f50b87c0b40d4c534", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12394, "upload_time": "2019-07-24T12:07:05", "url": "https://files.pythonhosted.org/packages/1a/ca/399ff253de77184d0217c0a6bae2282a9c4af12456e4a50fc9c1e2a9469f/restframework_datachange-0.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "28f058160ed32ebb2ac4fab1e0e45eb3", "sha256": "f21edbda876c825af9384f57342021cae5446926acaef52fb8ac7a8bff00429e" }, "downloads": -1, "filename": "restframework_datachange-0.0.1.tar.gz", "has_sig": false, "md5_digest": "28f058160ed32ebb2ac4fab1e0e45eb3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13500, "upload_time": "2019-07-24T12:07:08", "url": "https://files.pythonhosted.org/packages/b0/72/0bd65800ac9fbbdcbccd3ac12bbed6d65d20a83ca1f9483e616dbeae3d57/restframework_datachange-0.0.1.tar.gz" } ], "0.0.2.1": [ { "comment_text": "", "digests": { "md5": "8ae49a6aa9468198ce70fc2ed36bc159", "sha256": "f63d8275696a94c43195116b7477ceedb1060b8fe4442fa7b155e779d86e142f" }, "downloads": -1, "filename": "restframework_datachange-0.0.2.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8ae49a6aa9468198ce70fc2ed36bc159", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13170, "upload_time": "2019-07-25T06:17:07", "url": "https://files.pythonhosted.org/packages/1a/fd/01376b3d7c8d8b1a85ea37882fc8d91ce6a0339e51c31ce8898384f92c4a/restframework_datachange-0.0.2.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f45b7e1d460d14954a223e8c5da2bb67", "sha256": "cc9df70da9bfbb395e6f47b83f1e3fb9bbf3834b40bdc4f2845489b762bafcca" }, "downloads": -1, "filename": "restframework_datachange-0.0.2.1.tar.gz", "has_sig": false, "md5_digest": "f45b7e1d460d14954a223e8c5da2bb67", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15335, "upload_time": "2019-07-25T06:17:09", "url": "https://files.pythonhosted.org/packages/8c/3e/56435418e3b3aa3bff1debdccda07fb9a107fe1159552e010e5ae583971a/restframework_datachange-0.0.2.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8ae49a6aa9468198ce70fc2ed36bc159", "sha256": "f63d8275696a94c43195116b7477ceedb1060b8fe4442fa7b155e779d86e142f" }, "downloads": -1, "filename": "restframework_datachange-0.0.2.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8ae49a6aa9468198ce70fc2ed36bc159", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13170, "upload_time": "2019-07-25T06:17:07", "url": "https://files.pythonhosted.org/packages/1a/fd/01376b3d7c8d8b1a85ea37882fc8d91ce6a0339e51c31ce8898384f92c4a/restframework_datachange-0.0.2.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f45b7e1d460d14954a223e8c5da2bb67", "sha256": "cc9df70da9bfbb395e6f47b83f1e3fb9bbf3834b40bdc4f2845489b762bafcca" }, "downloads": -1, "filename": "restframework_datachange-0.0.2.1.tar.gz", "has_sig": false, "md5_digest": "f45b7e1d460d14954a223e8c5da2bb67", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15335, "upload_time": "2019-07-25T06:17:09", "url": "https://files.pythonhosted.org/packages/8c/3e/56435418e3b3aa3bff1debdccda07fb9a107fe1159552e010e5ae583971a/restframework_datachange-0.0.2.1.tar.gz" } ] }