Repository: dbrgn/drf-dynamic-fields
Branch: master
Commit: 2ddff1d6d54f
Files: 19
Total size: 24.5 KB
Directory structure:
gitextract_glgoqxjx/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.rst
├── RELEASING.md
├── drf_dynamic_fields/
│ └── __init__.py
├── manage.py
├── runtests.py
├── setup.cfg
├── setup.py
└── tests/
├── __init__.py
├── models.py
├── serializers.py
├── settings.py
├── test_mixins.py
├── test_requests.py
├── urls.py
└── views.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
on:
push:
branches:
- master
pull_request:
name: CI
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
deps:
- { python: '3.7', django: '~=2.2.0', drf: '~=3.10.0' }
- { python: '3.7', django: '~=3.2.0', drf: '~=3.11.0' }
- { python: '3.8', django: '~=3.2.0', drf: '~=3.12.0' }
- { python: '3.10', django: '~=3.2.0', drf: '~=3.12.0' }
- { python: '3.9', django: '~=4.0.0', drf: '~=3.13.0' }
- { python: '3.10', django: '~=4.0.0', drf: '~=3.13.0' }
fail-fast: false
name: Python ${{ matrix.deps.python }} / Django ${{ matrix.deps.django }} / DRF ${{ matrix.deps.drf }}
steps:
- uses: actions/checkout@v2
- name: Setup python ${{ matrix.deps.python }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.deps.python }}
- name: Upgrade pip
run: pip install -U pip
- name: Install dependencies
run: pip install "django${{ matrix.deps.django }}" "djangorestframework${{ matrix.deps.drf }}"
- name: Run tests
run: python runtests.py
check-formatting:
runs-on: ubuntu-latest
name: Check code formatting
steps:
- uses: actions/checkout@v2
- name: Black Code Formatter
uses: lgeiger/black-action@master
with:
args: "drf_dynamic_fields tests runtests.py --check --diff"
================================================
FILE: .gitignore
================================================
.cache/
.coverage
*.swp
*.pyc
__pycache__
dist/
*.egg-info/
build/
.tox/
================================================
FILE: CHANGELOG.md
================================================
# Changelog
This project follows semantic versioning.
Possible log types:
- `[added]` for new features.
- `[changed]` for changes in existing functionality.
- `[deprecated]` for once-stable features removed in upcoming releases.
- `[removed]` for deprecated features removed in this release.
- `[fixed]` for any bug fixes.
- `[security]` to invite users to upgrade in case of vulnerabilities.
## [Unreleased]
## [0.4.0] - 2022-04-05
- [added] Cache `fields` property for improved performance (#33, #35)
- [fixed] Fix warning message typo (#34)
- [changed] Python 2 support dropped
- [changed] Django <2.2 support dropped
## [0.3.1] - 2019-03-01
- [added] Allow suppressing context warnings (#27)
- [changed] Explicitly list supported versions in README
- [deprecated] Python 2 support will be dropped in version 0.4
## [0.3.0] - 2018-03-03
- [changed] Do not apply filter to nested serializers (#14)
## [0.2.0] - 2017-04-07
- [added] Add `omit` option to exclude fields (#11)
- [fixed] Make it work properly with nested serializers (#8, #10)
## [0.1.1] - 2016-10-16
- [fixed] Make it work in an unit test environment (#2)
## [0.1.0] - 2016-09-30
- Initial release
[Unreleased]: https://github.com/dbrgn/drf-dynamic-fields/compare/v0.4.0...HEAD
[0.3.1]: https://github.com/dbrgn/drf-dynamic-fields/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/dbrgn/drf-dynamic-fields/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/dbrgn/drf-dynamic-fields/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/dbrgn/drf-dynamic-fields/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/dbrgn/drf-dynamic-fields/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/dbrgn/drf-dynamic-fields/releases/tag/v0.1.0
================================================
FILE: LICENSE
================================================
Copyright (c) 2014--2016 Danilo Bargen and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.rst
================================================
Dynamic Serializer Fields for Django REST Framework
===================================================
.. image:: https://secure.travis-ci.org/dbrgn/drf-dynamic-fields.png?branch=master
:alt: Build status
:target: http://travis-ci.org/dbrgn/drf-dynamic-fields
.. image:: https://img.shields.io/pypi/v/drf-dynamic-fields.svg
:alt: PyPI Version
:target: https://pypi.python.org/pypi/drf-dynamic-fields
.. image:: https://img.shields.io/pypi/dm/drf-dynamic-fields.svg?maxAge=3600
:alt: PyPI Downloads
:target: https://pypi.python.org/pypi/drf-dynamic-fields
.. image:: https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000
:alt: License is MIT
:target: https://github.com/dbrgn/drf-dynamic-fields/blob/master/LICENSE
This package provides a mixin that allows the user to dynamically select only a
subset of fields per resource.
Official version support:
- Django 2.2 LTS, 3.2 LTS, 4.0
- Supported REST Framework versions: 3.8, 3.9
- Python 3.7+
Scope
-----
This library is about filtering fields based on individual requests. It is
deliberately kept simple and we do not plan to add new features (including
support for nested fields). Feel free to contribute improvements, code
simplifications and bugfixes though! (See also: `#18
<https://github.com/dbrgn/drf-dynamic-fields/issues/18>`__)
If you need more advanced filtering features, maybe `drf-flex-fields
<https://github.com/rsinger86/drf-flex-fields>`_ could be something for you.
Installing
----------
::
pip install drf-dynamic-fields
What It Does
------------
Example serializer:
.. sourcecode:: python
class IdentitySerializer(DynamicFieldsMixin, serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Identity
fields = ('id', 'url', 'type', 'data')
A regular request returns all fields:
``GET /identities``
.. sourcecode:: json
[
{
"id": 1,
"url": "http://localhost:8000/api/identities/1/",
"type": 5,
"data": "John Doe"
},
...
]
A query with the `fields` parameter on the other hand returns only a subset of
the fields:
``GET /identities/?fields=id,data``
.. sourcecode:: json
[
{
"id": 1,
"data": "John Doe"
},
...
]
And a query with the `omit` parameter excludes specified fields.
``GET /identities/?omit=data``
.. sourcecode:: json
[
{
"id": 1,
"url": "http://localhost:8000/api/identities/1/",
"type": 5
},
...
]
You can use both `fields` and `omit` in the same request!
``GET /identities/?omit=data,fields=data,id``
.. sourcecode:: json
[
{
"id": 1
},
...
]
Though why you would want to do something like that is beyond this author.
It also works on single objects!
``GET /identities/1/?fields=id,data``
.. sourcecode:: json
{
"id": 1,
"data": "John Doe"
}
Usage
-----
When defining a serializer, use the ``DynamicFieldsMixin``:
.. sourcecode:: python
from drf_dynamic_fields import DynamicFieldsMixin
class IdentitySerializer(DynamicFieldsMixin, serializers.ModelSerializer):
class Meta:
model = models.Identity
fields = ('id', 'url', 'type', 'data')
The mixin needs access to the ``request`` object. Some DRF classes like the
``ModelViewSet`` set that by default, but if you handle serializers yourself,
pass in the request through the context:
.. sourcecode:: python
events = Event.objects.all()
serializer = EventSerializer(events, many=True, context={'request': request})
Warnings
--------
If the request context does not have access to the request, a warning is
emitted::
UserWarning: Context does not have access to request.
First, make sure that you are passing the request to the serializer context (see
"Usage" section).
There are some cases (e.g. nested serializers) where you cannot get rid of the
warning that way (see `issue 27 <https://github.com/dbrgn/drf-dynamic-fields/issues/27>`_).
In that case, you can silence the warning through ``settings.py``:
.. sourcecode:: python
DRF_DYNAMIC_FIELDS = {
'SUPPRESS_CONTEXT_WARNING': True,
}
Testing
-------
To run tests, install Django and DRF and then run ``runtests.py``:
$ python runtests.py
Credits
-------
- The implementation is based on `this
<http://stackoverflow.com/a/23674297/284318>`__ StackOverflow answer. Thanks
``YAtOff``!
- The GitHub users ``X17`` and ``rawbeans`` provided improvements on `my gist
<https://gist.github.com/dbrgn/4e6fc1fe5922598592d6>`__ that were incorporated
into this library. Thanks!
- For other contributors, please see `Github contributor stats
<https://github.com/dbrgn/drf-dynamic-fields/graphs/contributors>`__.
License
-------
MIT license, see ``LICENSE`` file.
================================================
FILE: RELEASING.md
================================================
# Release process
Signing key: https://dbrgn.ch/F2F3A5FA.asc
Used variables:
export VERSION={VERSION}
export GPG=F2F3A5FA
Update version number in setup.py and CHANGELOG.md:
vim -p setup.py CHANGELOG.md
Do a signed commit and signed tag of the release:
git add setup.py CHANGELOG.md
git commit -S${GPG} -m "Release v${VERSION}"
git tag -u ${GPG} -m "Release v${VERSION}" v${VERSION}
Build source and binary distributions:
python3 setup.py sdist
python3 setup.py bdist_wheel
Sign files:
gpg --detach-sign -u ${GPG} -a dist/drf_dynamic_fields-${VERSION}.tar.gz
gpg --detach-sign -u ${GPG} -a dist/drf_dynamic_fields-${VERSION}-py2.py3-none-any.whl
Upload package to PyPI:
twine3 upload dist/drf_dynamic_fields-${VERSION}*
git push
git push --tags
================================================
FILE: drf_dynamic_fields/__init__.py
================================================
"""
Mixin to dynamically select only a subset of fields per DRF resource.
"""
import warnings
from django.conf import settings
from django.utils.functional import cached_property
class DynamicFieldsMixin(object):
"""
A serializer mixin that takes an additional `fields` argument that controls
which fields should be displayed.
"""
@cached_property
def fields(self):
"""
Filters the fields according to the `fields` query parameter.
A blank `fields` parameter (?fields) will remove all fields. Not
passing `fields` will pass all fields individual fields are comma
separated (?fields=id,name,url,email).
"""
fields = super(DynamicFieldsMixin, self).fields
if not hasattr(self, "_context"):
# We are being called before a request cycle
return fields
# Only filter if this is the root serializer, or if the parent is the
# root serializer with many=True
is_root = self.root == self
parent_is_list_root = self.parent == self.root and getattr(
self.parent, "many", False
)
if not (is_root or parent_is_list_root):
return fields
try:
request = self.context["request"]
except KeyError:
conf = getattr(settings, "DRF_DYNAMIC_FIELDS", {})
if not conf.get("SUPPRESS_CONTEXT_WARNING", False) is True:
warnings.warn(
"Context does not have access to request. "
"See README for more information."
)
return fields
# NOTE: drf test framework builds a request object where the query
# parameters are found under the GET attribute.
params = getattr(request, "query_params", getattr(request, "GET", None))
if params is None:
warnings.warn("Request object does not contain query parameters")
try:
filter_fields = params.get("fields", None).split(",")
except AttributeError:
filter_fields = None
try:
omit_fields = params.get("omit", None).split(",")
except AttributeError:
omit_fields = []
# Drop any fields that are not specified in the `fields` argument.
existing = set(fields.keys())
if filter_fields is None:
# no fields param given, don't filter.
allowed = existing
else:
allowed = set(filter(None, filter_fields))
# omit fields in the `omit` argument.
omitted = set(filter(None, omit_fields))
for field in existing:
if field not in allowed:
fields.pop(field, None)
if field in omitted:
fields.pop(field, None)
return fields
================================================
FILE: manage.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Django manage.py, only used for testing.
"""
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
================================================
FILE: runtests.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8
"""
Run tests with python runtests.py
Taken from the django cookiecutter project.
"""
import os
import sys
import django
from django.conf import settings
from django.test.utils import get_runner
def run_tests(*test_args):
if not test_args:
test_args = ["tests"]
os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings"
django.setup()
test_runner = get_runner(settings)()
failures = test_runner.run_tests(test_args)
sys.exit(bool(failures))
if __name__ == "__main__":
run_tests(*sys.argv[1:])
================================================
FILE: setup.cfg
================================================
[bdist_wheel]
universal=1
================================================
FILE: setup.py
================================================
from setuptools import setup
readme = open('README.rst').read()
setup(name='drf_dynamic_fields',
version='0.4.0',
description='Dynamically return subset of Django REST Framework serializer fields',
author='Danilo Bargen',
author_email='mail@dbrgn.ch',
url='https://github.com/dbrgn/drf-dynamic-fields',
packages=['drf_dynamic_fields'],
zip_safe=True,
include_package_data=True,
license='MIT',
keywords='drf restframework rest_framework django_rest_framework serializers',
long_description=readme,
classifiers=[
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Framework :: Django',
'Environment :: Web Environment',
],
)
================================================
FILE: tests/__init__.py
================================================
================================================
FILE: tests/models.py
================================================
"""
Some models for the tests. We are modelling a school.
"""
from django.db import models
class Teacher(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
class School(models.Model):
"""Schools just have teachers, no students."""
name = models.CharField(max_length=30)
teachers = models.ManyToManyField(Teacher)
================================================
FILE: tests/serializers.py
================================================
"""
For the tests.
"""
from rest_framework import serializers
from drf_dynamic_fields import DynamicFieldsMixin
from .models import Teacher, School
class TeacherSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
"""
The request_info field is to highlight the issue accessing request during
a nested serializer.
"""
request_info = serializers.SerializerMethodField()
class Meta:
model = Teacher
fields = ("id", "request_info", "age", "name")
def get_request_info(self, teacher):
"""
a meaningless method that attempts
to access the request object.
"""
request = self.context["request"]
return request.build_absolute_uri("/api/v1/teacher/{}".format(teacher.pk))
class SchoolSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
"""
Interesting enough serializer because the TeacherSerializer
will use ListSerializer due to the `many=True`
"""
teachers = TeacherSerializer(many=True, read_only=True)
class Meta:
model = School
fields = ("id", "teachers", "name")
================================================
FILE: tests/settings.py
================================================
# -*- coding: utf-8
"""
Settings for test.
"""
import django
DEBUG = True
USE_TZ = True
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "**************************************************"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
}
}
ROOT_URLCONF = "tests.urls"
INSTALLED_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sites",
"drf_dynamic_fields",
"tests",
]
SITE_ID = 1
MIDDLEWARE = ()
================================================
FILE: tests/test_mixins.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
test_drf-dynamic-fields
-----------
Tests for `drf-dynamic-fields` mixins
"""
from collections import OrderedDict
from django.test import TestCase, RequestFactory
from .serializers import SchoolSerializer, TeacherSerializer
from .models import Teacher, School
class TestDynamicFieldsMixin(TestCase):
"""
Test case for the DynamicFieldsMixin
"""
def test_removes_fields(self):
"""
Does it actually remove fields?
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?fields=id")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(set(serializer.fields.keys()), set(("id",)))
def test_fields_left_alone(self):
"""
What if no fields param is passed? It should not touch the fields.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(
set(serializer.fields.keys()), set(("id", "request_info", "age", "name"))
)
def test_fields_all_gone(self):
"""
If we pass a blank fields list, then no fields should return.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?fields")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(set(serializer.fields.keys()), set())
def test_ordinary_serializer(self):
"""
Check the full JSON output of the serializer.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?fields=id,age")
teacher = Teacher.objects.create(name="Susan", age=34)
serializer = TeacherSerializer(teacher, context={"request": request})
self.assertEqual(serializer.data, {"id": teacher.id, "age": teacher.age})
def test_omit(self):
"""
Check a basic usage of omit.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?omit=request_info")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(set(serializer.fields.keys()), set(("id", "name", "age")))
def test_omit_and_fields_used(self):
"""
Can they be used together.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?fields=id,request_info&omit=request_info")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(set(serializer.fields.keys()), set(("id",)))
def test_omit_everything(self):
"""
Can remove it all tediously.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?omit=id,request_info,age,name")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(set(serializer.fields.keys()), set())
def test_omit_nothing(self):
"""
Blank omit doesn't affect anything.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?omit")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(
set(serializer.fields.keys()), set(("id", "request_info", "name", "age"))
)
def test_omit_non_existant_field(self):
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?omit=pretend")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(
set(serializer.fields.keys()), set(("id", "request_info", "name", "age"))
)
def test_as_nested_serializer(self):
"""
Nested serializers are not filtered.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?fields=teachers")
school = School.objects.create(name="Python Heights High")
teachers = [
Teacher.objects.create(name="Shane", age=45),
Teacher.objects.create(name="Kaz", age=29),
]
school.teachers.add(*teachers)
serializer = SchoolSerializer(school, context={"request": request})
request_info = "http://testserver/api/v1/teacher/{}"
self.assertEqual(
serializer.data,
{
"teachers": [
OrderedDict(
[
("id", teachers[0].id),
("request_info", request_info.format(teachers[0].id)),
("age", teachers[0].age),
("name", teachers[0].name),
]
),
OrderedDict(
[
("id", teachers[1].id),
("request_info", request_info.format(teachers[1].id)),
("age", teachers[1].age),
("name", teachers[1].name),
]
),
],
},
)
def test_serializer_reuse_with_changing_request(self):
"""
`fields` is a cached property. Changing the request on an already
instantiated serializer will not result in a changed fields attribute.
This was a deliberate choice we have made in favor of speeding up
access to the slow `fields` attribute.
"""
rf = RequestFactory()
request = rf.get("/api/v1/schools/1/?fields=id")
serializer = TeacherSerializer(context={"request": request})
self.assertEqual(set(serializer.fields.keys()), {"id"})
# now change the request on this instantiated serializer.
request2 = rf.get("/api/v1/schools/1/?fields=id,name")
serializer.context["request"] = request2
self.assertEqual(set(serializer.fields.keys()), {"id"})
================================================
FILE: tests/test_requests.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
test_drf-dynamic-fields
------------
Test for the full request cycle using dynamic fields mixns
"""
from collections import OrderedDict
from django.test import TestCase, RequestFactory
from rest_framework.reverse import reverse
from .serializers import SchoolSerializer, TeacherSerializer
from .models import Teacher, School
class TestDynamicFieldsViews(TestCase):
"""
Testing using dynamic fields in request framework views.
"""
def setUp(self):
"""
Create some teachers and schools.
"""
teachers = [("Craig", 34), ("Kaz", 29), ("Sun", 62)]
schools = ["Python Heights High", "Ruby Consolidated", "Java Coffee School"]
t = [Teacher.objects.create(name=name, age=age) for name, age in teachers]
for name in schools:
s = School.objects.create(name=name)
s.teachers.add(*t)
def test_teacher_basic(self):
response = self.client.get(reverse("teacher-list"))
for teacher in response.data:
self.assertEqual(teacher.keys(), {"id", "request_info", "age", "name"})
def test_teacher_fields(self):
response = self.client.get(reverse("teacher-list"), {"fields": "id,age"})
for teacher in response.data:
self.assertEqual(teacher.keys(), {"id", "age"})
def test_teacher_omit(self):
response = self.client.get(reverse("teacher-list"), {"omit": "id,age"})
for teacher in response.data:
self.assertEqual(teacher.keys(), {"request_info", "name"})
def test_nested_teacher_fields(self):
response = self.client.get(reverse("school-list"), {"fields": "name,teachers"})
for school in response.data:
self.assertEqual(school.keys(), {"teachers", "name"})
self.assertEqual(
school["teachers"][0].keys(), {"id", "request_info", "age", "name"}
)
================================================
FILE: tests/urls.py
================================================
# -*- coding: utf-8
from rest_framework import routers
from .views import SchoolViewSet, TeacherViewSet
router = routers.SimpleRouter()
router.register("teachers", TeacherViewSet)
router.register("schools", SchoolViewSet)
urlpatterns = router.urls
================================================
FILE: tests/views.py
================================================
from rest_framework.viewsets import ModelViewSet
from .models import School, Teacher
from .serializers import SchoolSerializer, TeacherSerializer
class TeacherViewSet(ModelViewSet):
queryset = Teacher.objects.all()
serializer_class = TeacherSerializer
class SchoolViewSet(ModelViewSet):
queryset = School.objects.all()
serializer_class = SchoolSerializer
gitextract_glgoqxjx/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.rst
├── RELEASING.md
├── drf_dynamic_fields/
│ └── __init__.py
├── manage.py
├── runtests.py
├── setup.cfg
├── setup.py
└── tests/
├── __init__.py
├── models.py
├── serializers.py
├── settings.py
├── test_mixins.py
├── test_requests.py
├── urls.py
└── views.py
SYMBOL INDEX (30 symbols across 7 files)
FILE: drf_dynamic_fields/__init__.py
class DynamicFieldsMixin (line 10) | class DynamicFieldsMixin(object):
method fields (line 17) | def fields(self):
FILE: runtests.py
function run_tests (line 16) | def run_tests(*test_args):
FILE: tests/models.py
class Teacher (line 7) | class Teacher(models.Model):
class School (line 12) | class School(models.Model):
FILE: tests/serializers.py
class TeacherSerializer (line 11) | class TeacherSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
class Meta (line 19) | class Meta:
method get_request_info (line 23) | def get_request_info(self, teacher):
class SchoolSerializer (line 32) | class SchoolSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
class Meta (line 40) | class Meta:
FILE: tests/test_mixins.py
class TestDynamicFieldsMixin (line 18) | class TestDynamicFieldsMixin(TestCase):
method test_removes_fields (line 23) | def test_removes_fields(self):
method test_fields_left_alone (line 33) | def test_fields_left_alone(self):
method test_fields_all_gone (line 45) | def test_fields_all_gone(self):
method test_ordinary_serializer (line 55) | def test_ordinary_serializer(self):
method test_omit (line 67) | def test_omit(self):
method test_omit_and_fields_used (line 77) | def test_omit_and_fields_used(self):
method test_omit_everything (line 87) | def test_omit_everything(self):
method test_omit_nothing (line 97) | def test_omit_nothing(self):
method test_omit_non_existant_field (line 109) | def test_omit_non_existant_field(self):
method test_as_nested_serializer (line 118) | def test_as_nested_serializer(self):
method test_serializer_reuse_with_changing_request (line 160) | def test_serializer_reuse_with_changing_request(self):
FILE: tests/test_requests.py
class TestDynamicFieldsViews (line 20) | class TestDynamicFieldsViews(TestCase):
method setUp (line 25) | def setUp(self):
method test_teacher_basic (line 39) | def test_teacher_basic(self):
method test_teacher_fields (line 45) | def test_teacher_fields(self):
method test_teacher_omit (line 51) | def test_teacher_omit(self):
method test_nested_teacher_fields (line 57) | def test_nested_teacher_fields(self):
FILE: tests/views.py
class TeacherViewSet (line 7) | class TeacherViewSet(ModelViewSet):
class SchoolViewSet (line 12) | class SchoolViewSet(ModelViewSet):
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (27K chars).
[
{
"path": ".github/workflows/ci.yml",
"chars": 1420,
"preview": "on:\n push:\n branches:\n - master\n pull_request:\n\nname: CI\n\njobs:\n\n test:\n runs-on: ubuntu-latest\n strate"
},
{
"path": ".gitignore",
"chars": 73,
"preview": ".cache/\n.coverage\n*.swp\n*.pyc\n__pycache__\ndist/\n*.egg-info/\nbuild/\n.tox/\n"
},
{
"path": "CHANGELOG.md",
"chars": 1731,
"preview": "# Changelog\n\nThis project follows semantic versioning.\n\nPossible log types:\n\n- `[added]` for new features.\n- `[changed]`"
},
{
"path": "LICENSE",
"chars": 1080,
"preview": "Copyright (c) 2014--2016 Danilo Bargen and contributors\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "README.rst",
"chars": 4886,
"preview": "Dynamic Serializer Fields for Django REST Framework\n===================================================\n\n.. image:: http"
},
{
"path": "RELEASING.md",
"chars": 810,
"preview": "# Release process\n\nSigning key: https://dbrgn.ch/F2F3A5FA.asc\n\nUsed variables:\n\n export VERSION={VERSION}\n export "
},
{
"path": "drf_dynamic_fields/__init__.py",
"chars": 2828,
"preview": "\"\"\"\nMixin to dynamically select only a subset of fields per DRF resource.\n\"\"\"\nimport warnings\n\nfrom django.conf import s"
},
{
"path": "manage.py",
"chars": 320,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\nDjango manage.py, only used for testing.\n\"\"\"\nimport os\nimport sys\n\nif "
},
{
"path": "runtests.py",
"chars": 576,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8\n\"\"\"\nRun tests with python runtests.py\n\nTaken from the django cookiecutter proj"
},
{
"path": "setup.cfg",
"chars": 26,
"preview": "[bdist_wheel]\nuniversal=1\n"
},
{
"path": "setup.py",
"chars": 831,
"preview": "from setuptools import setup\n\nreadme = open('README.rst').read()\n\nsetup(name='drf_dynamic_fields',\n version='0.4.0'"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/models.py",
"chars": 369,
"preview": "\"\"\"\nSome models for the tests. We are modelling a school.\n\"\"\"\nfrom django.db import models\n\n\nclass Teacher(models.Model)"
},
{
"path": "tests/serializers.py",
"chars": 1120,
"preview": "\"\"\"\nFor the tests.\n\"\"\"\nfrom rest_framework import serializers\n\nfrom drf_dynamic_fields import DynamicFieldsMixin\n\nfrom ."
},
{
"path": "tests/settings.py",
"chars": 549,
"preview": "# -*- coding: utf-8\n\"\"\"\nSettings for test.\n\"\"\"\nimport django\n\nDEBUG = True\nUSE_TZ = True\n\n# SECURITY WARNING: keep the s"
},
{
"path": "tests/test_mixins.py",
"chars": 5934,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\ntest_drf-dynamic-fields\n-----------\n\nTests for `drf-dynamic-fields` m"
},
{
"path": "tests/test_requests.py",
"chars": 1950,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\ntest_drf-dynamic-fields\n------------\n\nTest for the full request cycle"
},
{
"path": "tests/urls.py",
"chars": 251,
"preview": "# -*- coding: utf-8\nfrom rest_framework import routers\n\nfrom .views import SchoolViewSet, TeacherViewSet\n\nrouter = route"
},
{
"path": "tests/views.py",
"chars": 376,
"preview": "from rest_framework.viewsets import ModelViewSet\n\nfrom .models import School, Teacher\nfrom .serializers import SchoolSer"
}
]
About this extraction
This page contains the full source code of the dbrgn/drf-dynamic-fields GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (24.5 KB), approximately 6.4k tokens, and a symbol index with 30 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.