adenh93 / django-typomatic Goto Github PK
View Code? Open in Web Editor NEWA simple solution for generating Typescript interfaces from your Django Rest Framework Serializers.
License: MIT License
A simple solution for generating Typescript interfaces from your Django Rest Framework Serializers.
License: MIT License
I am using this module djangorestframework-camel-case. With this all of the fields change to camel casing. I was wondering if that could also be implemented in this project? Are you open for pr's?
I have created my own custom command, and figured it could be of use to other people it currently goes through each installed app checks for serializers and will automatically apply the @ts_interafce decorator to them:
import importlib
import inspect
import os
from types import ModuleType
from django.apps import AppConfig
from django.conf import settings
from django.core.management.base import BaseCommand
from django_typomatic import ts_interface, get_ts
from rest_framework.serializers import Serializer
class Command(BaseCommand):
EXCLUDED_APPS = ['rest_framework']
help = 'Creates a TypeScript definition file for all models registered in DRF serializers under src/ts/@types/django-models.d.ts'
def create_model_mappings(self, app_config: AppConfig):
"""Updates the custom mappings with related models and their type in ts"""
mappings = {}
for model in app_config.get_models():
for field in model._meta.get_fields():
if field.related_model is not None:
ts_type = field.related_model.__name__
if field.many_to_many or field.one_to_many:
ts_type += '[]'
mappings[field.name] = ts_type
return mappings
def handle(self, *args, **options):
from django.apps import apps
for app_config in apps.get_app_configs():
if app_config.name not in self.EXCLUDED_APPS:
self.handle_app_config(app_config)
# Remove Serializer from type name since it's redundant in TS
ts = get_ts().replace('Serializer', '')
type_file_location = os.path.join(settings.BASE_DIR, 'src/ts/@types/django-models.d.ts')
with open(type_file_location, 'w') as type_file:
type_file.write(ts)
self.stdout.write(self.style.SUCCESS(f'Type file sucessfully generated at {type_file_location}'))
def handle_app_config(self, app_config: AppConfig):
try: #Check to see the app has serializers
serializers_module: ModuleType = importlib.import_module(app_config.name + '.serializers')
except ImportError:
return
mappings = self.create_model_mappings(app_config)
serializers = inspect.getmembers(serializers_module, lambda member: self.is_serializer(member, serializers_module))
for name, serializer in serializers:
# Get the class def and apply the ts_interface decorator to it
base_class = getattr(serializers_module, name)
ts_interface(mapping_overrides=mappings)(base_class)
def is_serializer(self, member: object, module):
"""Checks to see if the given member is a serializer class and is a part of the given module"""
return inspect.isclass(member) and issubclass(member, Serializer) and member.__module__ == module.__name__
Great library, has saved me a lot of time!
One issue I had is that it would use choices for PrimaryKeyRelatedField
- populating the types with a list of every possible ID in the database. This isn't desirable because there's no reason you should be referencing database IDs in your frontend code, and even if you were, it's likely your local dev environment where you're writing this will be out of sync with production.
My workaround to this was to do this in my management command where I generate the ts:
from django_typomatic import get_ts, ts_field
from rest_framework import serializers
ts_field("number")(serializers.PrimaryKeyRelatedField)
A more permanent solution could be to add an extra elif clause before the choices check to specifically check if it is a PrimaryKeyRelatedField, and then somehow figure out the type of the pk for that field (DRF doesn't store this anywhere I can see). Alternatively we could use number | string
as the type or just number
since that's how Django automatically generates IDs.
I am trying to generate enums for my choice fields in my models, but get a Value error when generate_ts() is called. This is also the case for the example from the README, for both "enumChoices" and "choices"
from django_typomatic import ts_interface, generate_ts
from rest_framework import serializers
from django.db import models
class ActionType(models.TextChoices):
ACTION1 = "Action1", ("Action1")
ACTION2 = "Action2", ("Action2")
ACTION3 = "Action3", ("Action3")
class NumberType(models.IntegerChoices):
LOW = 1
MEDIUM = 2
HIGH = 3
@ts_interface('enumChoices')
class ChoiceSerializer(serializers.Serializer):
action = serializers.ChoiceField(choices=ActionType.choices)
num = serializers.ChoiceField(choices=NumberType.choices)
generate_ts("some path relative to the root")
I get the following stacktrace:
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/usr/local/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.11/threading.py", line 975, in run
self._target(*self._args, **self._kwargs)
File "/opt/venv/lib/python3.11/site-packages/django/utils/autoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "/opt/venv/lib/python3.11/site-packages/django/core/management/commands/runserver.py", line 125, in inner_run
autoreload.raise_last_exception()
File "/opt/venv/lib/python3.11/site-packages/django/utils/autoreload.py", line 87, in raise_last_exception
raise _exception[1]
File "/opt/venv/lib/python3.11/site-packages/django/core/management/init.py", line 398, in execute
autoreload.check_errors(django.setup)()
File "/opt/venv/lib/python3.11/site-packages/django/utils/autoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "/opt/venv/lib/python3.11/site-packages/django/init.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/opt/venv/lib/python3.11/site-packages/django/apps/registry.py", line 124, in populate
app_config.ready()
File "/app/backend/asset/apps.py", line 9, in ready
import backend.asset.serializers
File "/app/backend/asset/serializers.py", line 4, in
from config.typomatic import generate_ts_types
File "/app/config/typomatic.py", line 34, in
generate_ts_types()
File "/app/config/typomatic.py", line 8, in generate_ts_types
generate_ts("./frontend/src/drf.d.ts")
File "/opt/venv/lib/python3.11/site-packages/django_typomatic/init.py", line 219, in generate_ts
enums_string, interfaces = __get_enums_and_interfaces_from_generated(interfaces_enums)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/venv/lib/python3.11/site-packages/django_typomatic/init.py", line 196, in __get_enums_and_interfaces_from_generated
interfaces, enums = [list(tup) for tup in zip(*interfaces_enums)]
^^^^^^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 0)
I am running all of this in a Docker container, but everything annotated with just ts_interface
works fine
In our project we have the need to create typescript interfaces for complex serializers, which include the following:
class MeteringPointSerializer(serializers.ModelSerializer):
# some fields...
vacancy_time_slices = serializers.SerializerMethodField()
def get_vacancy_time_slices(self, instance) -> List[Tuple[date, date]]:
# some implementation returning a list of tuples....
# rest of the serializer
Trying to create typescript interface for this serializer results in the following error:
Traceback (most recent call last):
File "/workspace/api/app/./manage_debug.py", line 15, in <module>
execute_from_command_line(sys.argv)
File "/workspace/api/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
utility.execute()
File "/workspace/api/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/workspace/api/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 354, in run_from_argv
self.execute(*args, **cmd_options)
File "/workspace/api/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 398, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/management/commands/generate_ts.py", line 147, in handle
self._generate_ts(app_name, serializer_name, output, **options)
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/management/commands/generate_ts.py", line 114, in _generate_ts
generate_ts(
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/__init__.py", line 513, in generate_ts
interfaces_enums = __generate_interfaces_and_enums(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/__init__.py", line 416, in __generate_interfaces_and_enums
return [
^
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/__init__.py", line 417, in <listcomp>
__get_ts_interface_and_enums(
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/__init__.py", line 373, in __get_ts_interface_and_enums
ts_property, ts_type, ts_enum, ts_enum_value = __process_field(
^^^^^^^^^^^^^^^^
File "/workspace/api/.venv/lib/python3.11/site-packages/django_typomatic/__init__.py", line 248, in __process_field
if issubclass(return_type, BaseSerializer):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: issubclass() arg 1 must be a class
After some debugging I discovered, that it was because of the TODO comments topic of allowing nested iterables.
It would be awesome to have support for this.
For TypeScript v3 or higher we should be using unknown
as a type instead of any
when we can't find an appropriate type to map to. You can view the TypeScript PR that introduces unknown
and its purpose here.
PR incoming....
HI! I worked with this package for a project and it was really great all the time it saved me, now I am working on a different project that sadly has too old version of both Dango and DRF, even Python to be honest (Python ~2.7
). So I was wondering if it would be too difficult to make support for older versions of Django and DRF. I've never tried to update or in this case make support for older versions of a package so I don't know if it is difficult. I really want to use this package on this project because the time it would save on creating the types would be insane.
django==1.8.19
djangorestframework==2.3.9
I also looked. for other options online because I thought that maybe there were similar packages for the same thing that supported older versions but I. didn't find anything.
I'd like to be able to compile typescript union types. Something like what's below.
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
type Animal = Bird | Fish;
Consider the following enum:
class ClassLevel(models.IntegerChoices):
UNKNOWN = -100, "Unknown"
UNKNOWN_FIRST_OR_LOWER = -99, "Unknown (Class 1 or lower)"
LOWER_KINDERGARTEN = -1, "LKG"
UPPER_KINDERGARTEN = 0, "UKG"
FIRST = 1, "1"
SECOND = 2, "2"
THIRD = 3, "3"
FOURTH = 4, "4"
FIFTH = 5, "5"
SIXTH = 6, "6"
SEVENTH = 7, "7"
EIGHTH = 8, "8"
NINTH = 9, "9"
TENTH = 10, "10"
ELEVENTH = 11, "11"
TWELFTH = 12, "12"
UNKNOWN_TWELFTH_OR_HIGHER = 13, "Unknown (12+)"
When generating types for this I would like the enum to be created with the python enum name instead of taking the display name or value and trying to capitalize it.
Trying to capitalize display name or value and have it as a key in typescript enums leaves it with parentheses and may not be valid typescript depending on special characters.
I would like the following enum:
export enum CurrentClassChoiceEnum {
UNKNOWN = -100,
UNKNOWN_FIRST_OR_LOWER = -99
...
}
Instead of the current:
export enum CurrentClassChoiceEnum {
UNKNOWN = -100,
UNKNOWN_(CLASS_1_OR_LOWER) = -99,
...
}
Example for how to get python enum names:
choices = ClassLevel.choices
print("Python enum name,", "Value,", "Display name")
for choice in choices:
print(ClassLevel(choice[0]).name, choice[0], choice[1])
I really like this library for copying and pasting types into my UI (I do a find/replace for 'Serializer' and replace it with nothing and commit it to my UI codebase).
However, I'm running into a problem with the builtin Permissions model.
# serializers.py
from django.contrib.auth.models import Permission
from django_typomatic import ts_interface
from rest_framework.serializers import ModelSerializer
@ts_interface
class PermissionSerializer(ModelSerializer):
class Meta:
model = Permission
fields = '__all__'
# views.py
@api_view(["GET"])
@permission_classes((permissions.IsAuthenticated,))
def get_all_permissions_for_groups(request):
all_permissions = Permission.objects \
.prefetch_related('group_set', 'content_type') \
.filter(group__user=request.user
)
serializer = PermissionSerializer(all_permissions, many=True)
return JsonResponse(serializer.data, safe=False)
Expected output:
[]
# or list of Permission objects of the groups I am part of
Actual output:
<SNIP>
File "C:\Users\bradl\lji\projects\childlight_django\app\rest\permissions\views.py", line 34, in get_all_permissions
serializer = PermissionSerializer(all_permissions, many=True)
TypeError: decorator() got an unexpected keyword argument 'many'
commenting out the @ts_interface annotation results in the expected output
Had a problem in __process_field
: elif field_type in __field_mappings[context]:
it was expecting the key 'default' exist in __field_mappings
, but __field_mappings
was populated only in ts_field.decorator
which I was not using.
Solved it by adding the same population logic in ts_interface.decorator
:
def decorator(cls):
if issubclass(cls, serializers.Serializer):
if context not in __field_mappings:
__field_mappings[context] = dict()
if context not in __serializers:
__serializers[context] = []
__serializers[context].append(cls)
and it worked :)
It would be nice to have the option to generate mypy types in addition to TypeScript definitions.
The use case would allow you to pass around typed dictionaries in Python code that is either passed into or returned from a DRF Serializer.
#33 created difficulties when combining Django Typomatic with https://github.com/jazzband/django-simple-history. This issue is because of the following;
export enum HistoryTypeChoiceEnum {
+ = '+',
~ = '~',
- = '-',
}
Using the new enum_values
option doesn't resolve this since the following code also is incorrect:
export enum HistoryTypeChoiceEnumValues {
+ = 'Created',
~ = 'Changed',
- = 'Deleted',
}
The solution of version 1.7.0 does work since enum_choices
gave:
export enum HistoryTypeChoiceEnum {
CREATED = '+',
CHANGED = '~',
DELETED = '-',
}
For now, I can stick to version 1.7.0 but might be nice to give an update
We've been using this library in production for a while now, and I just want to express my gratitude to the developer(s). It's made my work as a full-stack developer much more enjoyable. I don't have a lot of time on my hands to formally contribute to this project but I would like to just dump my hacked-together method that wraps get_ts() and writes Typescript files here, perhaps if someone finds this interesting they can distill the shortcomings I've encountered that this method works around.
from django_typomatic import (
__field_mappings,
__mapping_overrides,
__serializers,
get_ts,
)
import re, sys
# This method is called at the bottom of each of my serializers.py files
def write_typescript(file, imports=[], renames=[]):
if (
settings.DEBUG
and settings.GENERATE_TYPESCRIPT_SOURCES
and len(sys.argv) >= 2
and sys.argv[0] == "manage.py"
and sys.argv[1] == "runserver"
):
try:
f = open(f"/types/{file}.ts", "w") # /types is mounted (via docker) to a folder shared between backend and frontend sources, so frontend can directly use these generated files
ts = re.sub(r"[\?]", "", get_ts()).replace('"', "'")
for rename in renames:
ts = ts.replace(f"{rename[0]}Serializer {{", f"{rename[1]} {{")
ts = (
ts.replace("Serializer", "")
.replace(" File ", " string ")
.replace(" File;\n", " string;\n")
.replace(": number | string | null;\n", ": number | null;\n")
.replace(": number | string;\n", ": number;\n")
)
# Allow for some customizations
ts = re.sub(r"export interface ([_A-Za-z0-9]+) \{", r"export type \g<1> = {", ts)
matches = re.findall(r"export type ([_A-Za-z0-9]+) = \{\n(.+?)\}\n\n", ts, re.DOTALL)
for match in matches:
interface_name = match[0]
for rename in renames:
if interface_name == rename[1]:
interface_name = rename[0]
break
interface_body_original = f"export type {match[0]} = {{\n{match[1]}}}\n\n"
interface_body = interface_body_original
for serializer in __serializers["default"]:
if serializer.__name__ == interface_name + "Serializer":
serializer_fields = serializer().fields
field_matches = re.findall(r" ([a-z0-9_]+): (.+);\n", match[1])
for field_match in field_matches:
serializer_field = serializer_fields[field_match[0]]
if isinstance(serializer_field, serializers.ChoiceField):
if serializer_field.allow_blank:
interface_body = interface_body.replace(
f" {field_match[0]}: {field_match[1]};\n",
f" {field_match[0]}: {field_match[1]} | '';\n",
)
if isinstance(serializer_field, serializers.ManyRelatedField):
interface_body = interface_body.replace(
f" {field_match[0]}: {field_match[1]};\n",
f" {field_match[0]}: number[];\n",
)
break
ts = ts.replace(interface_body_original, interface_body)
f.write("/* eslint-disable prettier/prettier */\n\n")
if imports:
for line in imports:
f.write(f"{line}\n")
f.write("\n")
f.write(ts)
f.close()
__serializers.clear()
__field_mappings.clear()
__mapping_overrides.clear()
except PermissionError:
print("Unable to generate TypeScript sources")
e.g. UserFilterSerializer should simply become UserFilter
I've rewritten and monkey patched my own instance -- but maybe you want to incorporate it as well?
def alternate_get_ts_interface(serializer, context):
'''
Generates and returns a Typescript Interface by iterating
through the serializer fields of the DRF Serializer class
passed in as a parameter, and mapping them to the appropriate Typescript
data type.
'''
name = serializer.__name__
django_typomatic._LOG.debug(f"Creating interface for {name}")
fields = []
if issubclass(serializer, serializers.ModelSerializer):
instance = serializer()
fields = instance.get_fields().items()
else:
fields = serializer._declared_fields.items()
ts_fields = []
for key, value in fields:
ts_field = django_typomatic.__process_field(key, value, context, serializer)
if value.read_only or not value.required:
op = '?:'
else:
op = ':'
ts_fields.append(f" {ts_field[0]}{op} {ts_field[1]};")
collapsed_fields = '\n'.join(ts_fields)
return f'export interface {name} {{\n{collapsed_fields}\n}}\n\n'
I have a FileField
flyer = models.FileField(
_("Flyer"), upload_to="uploads/flyers", max_length=100, null=True, blank=True
)
and the type generated looks like this:
flyer?: File | null;
Shouldn't it just be a string?
AttributeError: module 'rest_framework.serializers' has no attribute 'NullBooleanField'
is raised since django-rest-framework version 3.14.0
I have a serializer named MySerializer in my_app/my_submodule/serializers.py
Unfortunately, the command line is not recursive, so using ./manage.py generate_ts -s my_app
does not work as my_app doesn't have a serializers.py file within it.
./manage.py generate_ts -s my_app.my_submodule
also does work just as above, because the command like thinks my_submodule is the class name of the serializer.
./manage.py generate_ts -s my_app.my_submodule.serializers.MySerializer
and ./manage.py generate_ts -s my_app.my_submodule.MySerializer
both fail because the commandline does not expect submodules.
I would like to propose that ./manage.py generate_ts -s my_app
does a recursive search for files named serializers.py and that ./manage.py generate_ts -s my_app.my_submodule
should check for a python module, and if it finds one, do a recursive search for serializers.py files in that module (otherwise it would fall back to checking if my_app.serializers.py exists and has a my_submodule class name)
There are some outputs produced like this:
any
export enum NumChoiceEnum {
LOW = 1,
MEDIUM = 2,
HIGH = 3,
}
export enum NumChoiceEnumKeys {
LOW = 1,
MEDIUM = 2,
HIGH = 3,
}
export enum StatusChoiceEnum {
1 = '1',
2 = '2',
3 = '3',
4 = '4',
5 = '5',
6 = '6',
}
export enum StatusChoiceEnumKeys {
ARCHIVED = '1',
PRE-SAVED = '2',
SAVED = '3',
USER'S_LIKES = '4',
USER'S_DISLIKES = '5',
NONE = '6',
}
export enum StatusChoiceEnumValues {
1 = 'Archived',
2 = 'Pre-saved',
3 = 'Saved',
4 = 'User\'s likes',
5 = 'User\'s dislikes',
6 = 'None',
}
export interface AlbumSerializer {
id?: number;
tracks?: TracksChoiceEnum[];
num: NumChoiceEnum;
album_name: string;
artist: string;
status: StatusChoiceEnum;
}
The "any" in the first line comes from the TracksChoiceEnum not having any database entries (thus having no choices). If there would be database entries, those would be printed as enum choices. This is undesired behavior since the database entries should not be printed as types.
This should be fixed by not checking if a field has the attribute choices
but some other element (like choice_strings_to_values
) which is not an attribute of a Related Field.
The produced output should look as follows:
export enum NumChoiceEnum {
LOW = 1,
MEDIUM = 2,
HIGH = 3,
}
export enum NumChoiceEnumKeys {
LOW = 1,
MEDIUM = 2,
HIGH = 3,
}
export enum StatusChoiceEnum {
'1' = '1',
'2' = '2',
'3' = '3',
'4' = '4',
'5' = '5',
'6' = '6',
}
export enum StatusChoiceEnumKeys {
'ARCHIVED' = '1',
'PRE-SAVED' = '2',
'SAVED' = '3',
'USER\'S_LIKES' = '4',
'USER\'S_DISLIKES' = '5',
'NONE' = '6',
}
export enum StatusChoiceEnumValues {
'1' = 'Archived',
'2' = 'Pre-saved',
'3' = 'Saved',
'4' = 'User\'s likes',
'5' = 'User\'s dislikes',
'6' = 'None',
}
export interface AlbumSerializer {
id?: number;
tracks?: any[];
num: NumChoiceEnum;
album_name: string;
artist: string;
status: StatusChoiceEnum;
}
(The models used can be found in #50)
๐๐ผ @adenh93
Thanks for this utility! I noticed that you dropped a new release a few days ago. Do you plan to release this onto pypi
so that we can pip install
the new versions and test drive it out?
Hey!
Hope you're doing well. Is there any chance you'd be willing to relicense (or perhaps dual license) your package to MIT/BSD? There's a bit of an aversion at my company to using GPL on certain projects so it'd help greatly with buy in.
Thank you
In your code, you go after _declared_fields
, but the Serializer implementation actually implements get_fields()
. Bypassing this method makes it difficult to derive my own Serializer that builds its own fields.
django-typomatic/django_typomatic/__init__.py
Line 103 in bd316dd
In other words, I think these two cases are the same and should just do the ModelSerializer method?:
if issubclass(serializer, serializers.ModelSerializer):
instance = serializer()
fields = instance.get_fields().items()
else:
fields = serializer._declared_fields.items()
Is there Django 5.0 support planned and when if so?
17.28 ERROR: Cannot install -r /requirements.txt (line 11), -r /requirements.txt (line 12), -r /requirements.txt (line 13) and django~=5.0 because these package versions have conflicting dependencies.
17.28
17.28 The conflict is caused by:
17.28 The user requested django~=5.0
17.28 djangorestf
[2023-12-28T09:14:42.188Z] ramework 3.14.0 depends on django>=3.0
17.28 django-cors-headers 4.3.1 depends on Django>=3.2
17.28 drf-typescript-generator 0.1.1 depends on Django<4.0.0 and >=3.2.4
Given the following code:
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from django.db import models
@ts_interface()
class ActionType(models.TextChoices):
ACTION1 = "Action1", _("Action1")
ACTION2 = "Action2", _("Action2")
@ts_interface()
class ActionSerializer(serializers.Serializer[Any]):
action = serializers.ChoiceField(choices=ActionType.choices)
The output of conversion is:
export interface ActionSerializer {
action: any;
-> wrong!
}
I would expect 'action' to be a union class of 'Action1' | 'Action2'
or 'ACTION1' | 'ACTION2'
. Am I using django-typomatic incorrectly or is this simply a limitation of the library?
Thanks!
>virtualenv-3.6.8/lib64/python3.6/site-packages/django_typomatic/__init__.py(75)__process_field()
74 import ipdb; ipdb.set_trace()
---> 75 is_many = hasattr(field, 'child')
76 field_type = is_many and type(field.child) or type(field)
ipdb> field
ManyRelatedField(child_relation=PrimaryKeyRelatedField(read_only=True, source='testdetails'), read_only=True, source='testdetails')
ipdb> field.child
*** AttributeError: 'ManyRelatedField' object has no attribute 'child'
ipdb> field
ManyRelatedField(child_relation=PrimaryKeyRelatedField(read_only=True, source='testdetails'), read_only=True, source='testdetails')
ipdb>
ManyRelatedField(child_relation=PrimaryKeyRelatedField(read_only=True, source='testdetails'), read_only=True, source='testdetails')
ipdb> field.child_relation
PrimaryKeyRelatedField(read_only=True, source='testdetails')
ipdb>
(Pull request pending)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.