mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-18 04:02:35 +03:00
django-upgrade --target-version=5.0 **/*.py
This commit is contained in:
parent
74689b1f44
commit
d4dfcb5690
|
@ -7,7 +7,6 @@ ______ _____ _____ _____ __
|
|||
\_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_|
|
||||
"""
|
||||
|
||||
import django
|
||||
|
||||
__title__ = 'Django REST framework'
|
||||
__version__ = '3.14.0'
|
||||
|
@ -25,10 +24,6 @@ HTTP_HEADER_ENCODING = 'iso-8859-1'
|
|||
ISO_8601 = 'iso-8601'
|
||||
|
||||
|
||||
if django.VERSION < (3, 2):
|
||||
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
|
||||
|
||||
|
||||
class RemovedInDRF315Warning(DeprecationWarning):
|
||||
pass
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ def get_authorization_header(request):
|
|||
|
||||
Hide some test client ickyness where the header can be unicode.
|
||||
"""
|
||||
auth = request.META.get('HTTP_AUTHORIZATION', b'')
|
||||
auth = request.headers.get('authorization', b'')
|
||||
if isinstance(auth, str):
|
||||
# Work around django test client oddness
|
||||
auth = auth.encode(HTTP_HEADER_ENCODING)
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import django
|
||||
|
||||
if django.VERSION < (3, 2):
|
||||
default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig'
|
|
@ -21,6 +21,7 @@ class TokenChangeList(ChangeList):
|
|||
current_app=self.model_admin.admin_site.name)
|
||||
|
||||
|
||||
@admin.register(TokenProxy)
|
||||
class TokenAdmin(admin.ModelAdmin):
|
||||
list_display = ('key', 'user', 'created')
|
||||
fields = ('user',)
|
||||
|
@ -50,6 +51,3 @@ class TokenAdmin(admin.ModelAdmin):
|
|||
# Map back to actual Token, since delete() uses pk.
|
||||
token = Token.objects.get(key=obj.key)
|
||||
return super().delete_model(request, token)
|
||||
|
||||
|
||||
admin.site.register(TokenProxy, TokenAdmin)
|
||||
|
|
|
@ -145,29 +145,10 @@ else:
|
|||
return False
|
||||
|
||||
|
||||
if django.VERSION >= (4, 2):
|
||||
# Django 4.2+: use the stock parse_header_parameters function
|
||||
# Note: Django 4.1 also has an implementation of parse_header_parameters
|
||||
# which is slightly different from the one in 4.2, it needs
|
||||
# the compatibility shim as well.
|
||||
from django.utils.http import parse_header_parameters
|
||||
else:
|
||||
# Django <= 4.1: create a compatibility shim for parse_header_parameters
|
||||
from django.http.multipartparser import parse_header
|
||||
|
||||
def parse_header_parameters(line):
|
||||
# parse_header works with bytes, but parse_header_parameters
|
||||
# works with strings. Call encode to convert the line to bytes.
|
||||
main_value_pair, params = parse_header(line.encode())
|
||||
return main_value_pair, {
|
||||
# parse_header will convert *some* values to string.
|
||||
# parse_header_parameters converts *all* values to string.
|
||||
# Make sure all values are converted by calling decode on
|
||||
# any remaining non-string values.
|
||||
k: v if isinstance(v, str) else v.decode()
|
||||
for k, v in params.items()
|
||||
}
|
||||
|
||||
# Django 4.2+: use the stock parse_header_parameters function
|
||||
# Note: Django 4.1 also has an implementation of parse_header_parameters
|
||||
# which is slightly different from the one in 4.2, it needs
|
||||
# the compatibility shim as well.
|
||||
|
||||
if django.VERSION >= (5, 1):
|
||||
# Django 5.1+: use the stock ip_address_validators function
|
||||
|
|
|
@ -93,5 +93,5 @@ class DefaultContentNegotiation(BaseContentNegotiation):
|
|||
Given the incoming request, return a tokenized list of media
|
||||
type strings.
|
||||
"""
|
||||
header = request.META.get('HTTP_ACCEPT', '*/*')
|
||||
header = request.headers.get('accept', '*/*')
|
||||
return [token.strip() for token in header.split(',')]
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import io
|
||||
from importlib import import_module
|
||||
|
||||
import django
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
|
@ -394,19 +393,7 @@ class URLPatternsTestCase(testcases.SimpleTestCase):
|
|||
|
||||
cls._override.enable()
|
||||
|
||||
if django.VERSION > (4, 0):
|
||||
cls.addClassCleanup(cls._override.disable)
|
||||
cls.addClassCleanup(cleanup_url_patterns, cls)
|
||||
|
||||
super().setUpClass()
|
||||
|
||||
if django.VERSION < (4, 0):
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super().tearDownClass()
|
||||
cls._override.disable()
|
||||
|
||||
if hasattr(cls, '_module_urlpatterns'):
|
||||
cls._module.urlpatterns = cls._module_urlpatterns
|
||||
else:
|
||||
del cls._module.urlpatterns
|
||||
|
|
|
@ -26,7 +26,7 @@ class BaseThrottle:
|
|||
if present and number of proxies is > 0. If not use all of
|
||||
HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
|
||||
"""
|
||||
xff = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
xff = request.headers.get('x-forwarded-for')
|
||||
remote_addr = request.META.get('REMOTE_ADDR')
|
||||
num_proxies = api_settings.NUM_PROXIES
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import base64
|
||||
|
||||
import django
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
@ -236,9 +235,6 @@ class SessionAuthTests(TestCase):
|
|||
Regression test for #6088
|
||||
"""
|
||||
# Remove this shim when dropping support for Django 3.0.
|
||||
if django.VERSION < (3, 1):
|
||||
from django.middleware.csrf import _get_new_csrf_token
|
||||
else:
|
||||
from django.middleware.csrf import (
|
||||
_get_new_csrf_string, _mask_cipher_secret
|
||||
)
|
||||
|
|
|
@ -87,9 +87,6 @@ def pytest_configure(config):
|
|||
import rest_framework
|
||||
settings.STATIC_ROOT = os.path.join(os.path.dirname(rest_framework.__file__), 'static-root')
|
||||
backend = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
|
||||
if django.VERSION < (4, 2):
|
||||
settings.STATICFILES_STORAGE = backend
|
||||
else:
|
||||
settings.STORAGES['staticfiles']['BACKEND'] = backend
|
||||
|
||||
django.setup()
|
||||
|
|
|
@ -4,7 +4,7 @@ import unittest
|
|||
|
||||
from django.http import HttpResponse
|
||||
from django.test import override_settings
|
||||
from django.urls import path, re_path
|
||||
from django.urls import path
|
||||
|
||||
from rest_framework.compat import coreapi, coreschema
|
||||
from rest_framework.parsers import FileUploadParser
|
||||
|
@ -180,7 +180,7 @@ class HeadersView(APIView):
|
|||
urlpatterns = [
|
||||
path('', SchemaView.as_view()),
|
||||
path('example/', ListView.as_view()),
|
||||
re_path(r'^example/(?P<id>[0-9]+)/$', DetailView.as_view()),
|
||||
path('example/<int:id>/', DetailView.as_view()),
|
||||
path('upload/', UploadView.as_view()),
|
||||
path('download/', DownloadView.as_view()),
|
||||
path('text/', TextView.as_view()),
|
||||
|
|
|
@ -65,7 +65,7 @@ class TestMiddleware(APITestCase):
|
|||
key = 'abcd1234'
|
||||
Token.objects.create(key=key, user=user)
|
||||
|
||||
self.client.get('/auth', HTTP_AUTHORIZATION='Token %s' % key)
|
||||
self.client.get('/auth', headers={"authorization": 'Token %s' % key})
|
||||
|
||||
@override_settings(MIDDLEWARE=('tests.test_middleware.RequestPOSTMiddleware',))
|
||||
def test_middleware_can_access_request_post_when_processing_response(self):
|
||||
|
|
|
@ -11,7 +11,6 @@ import json # noqa
|
|||
import sys
|
||||
import tempfile
|
||||
|
||||
import django
|
||||
import pytest
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
@ -454,8 +453,6 @@ class TestPosgresFieldsMapping(TestCase):
|
|||
fields = ['array_field', 'array_field_with_blank']
|
||||
|
||||
validators = ""
|
||||
if django.VERSION < (4, 1):
|
||||
validators = ", validators=[<django.core.validators.MaxLengthValidator object>]"
|
||||
expected = dedent("""
|
||||
TestSerializer():
|
||||
array_field = ListField(allow_empty=False, child=CharField(label='Array field'%s))
|
||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
from _pytest.monkeypatch import MonkeyPatch
|
||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||
from django.test import override_settings
|
||||
from django.urls import re_path
|
||||
from django.urls import path
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
|
||||
from rest_framework import relations, serializers
|
||||
|
@ -152,7 +152,7 @@ class TestProxiedPrimaryKeyRelatedField(APISimpleTestCase):
|
|||
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^example/(?P<name>.+)/$', lambda: None, name='example'),
|
||||
path('example/<path:name>/', lambda: None, name='example'),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ class RendererEndToEndTests(TestCase):
|
|||
|
||||
def test_default_renderer_serializes_content_on_accept_any(self):
|
||||
"""If the Accept header is set to */* the default renderer should serialize the response."""
|
||||
resp = self.client.get('/', HTTP_ACCEPT='*/*')
|
||||
resp = self.client.get('/', headers={"accept": '*/*'})
|
||||
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
@ -182,7 +182,7 @@ class RendererEndToEndTests(TestCase):
|
|||
def test_specified_renderer_serializes_content_default_case(self):
|
||||
"""If the Accept header is set the specified renderer should serialize the response.
|
||||
(In this case we check that works for the default renderer)"""
|
||||
resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type)
|
||||
resp = self.client.get('/', headers={"accept": RendererA.media_type})
|
||||
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
@ -190,14 +190,14 @@ class RendererEndToEndTests(TestCase):
|
|||
def test_specified_renderer_serializes_content_non_default_case(self):
|
||||
"""If the Accept header is set the specified renderer should serialize the response.
|
||||
(In this case we check that works for a non-default renderer)"""
|
||||
resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type)
|
||||
resp = self.client.get('/', headers={"accept": RendererB.media_type})
|
||||
self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
||||
def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
|
||||
"""If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
|
||||
resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
|
||||
resp = self.client.get('/', headers={"accept": 'foo/bar'})
|
||||
self.assertEqual(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE)
|
||||
|
||||
def test_specified_renderer_serializes_content_on_format_query(self):
|
||||
|
@ -228,14 +228,14 @@ class RendererEndToEndTests(TestCase):
|
|||
RendererB.format
|
||||
)
|
||||
resp = self.client.get('/' + param,
|
||||
HTTP_ACCEPT=RendererB.media_type)
|
||||
headers={"accept": RendererB.media_type})
|
||||
self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
||||
def test_parse_error_renderers_browsable_api(self):
|
||||
"""Invalid data should still render the browsable API correctly."""
|
||||
resp = self.client.post('/parseerror', data='foobar', content_type='application/json', HTTP_ACCEPT='text/html')
|
||||
resp = self.client.post('/parseerror', data='foobar', content_type='application/json', headers={"accept": 'text/html'})
|
||||
self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8')
|
||||
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
@ -714,13 +714,13 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
|
|||
assert result is None
|
||||
|
||||
def test_extra_actions_dropdown(self):
|
||||
resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html')
|
||||
resp = self.client.get('/api/examples/', headers={"accept": 'text/html'})
|
||||
assert 'id="extra-actions-menu"' in resp.content.decode()
|
||||
assert '/api/examples/list_action/' in resp.content.decode()
|
||||
assert '>Extra list action<' in resp.content.decode()
|
||||
|
||||
def test_extra_actions_dropdown_not_authed(self):
|
||||
resp = self.client.get('/api/unauth-examples/', HTTP_ACCEPT='text/html')
|
||||
resp = self.client.get('/api/unauth-examples/', headers={"accept": 'text/html'})
|
||||
assert 'id="extra-actions-menu"' not in resp.content.decode()
|
||||
assert '/api/examples/list_action/' not in resp.content.decode()
|
||||
assert '>Extra list action<' not in resp.content.decode()
|
||||
|
|
|
@ -28,7 +28,7 @@ class Root(APIView):
|
|||
}
|
||||
post = request.POST
|
||||
json = None
|
||||
if request.META.get('CONTENT_TYPE') == 'application/json':
|
||||
if request.headers.get('content-type') == 'application/json':
|
||||
json = request.data
|
||||
|
||||
return Response({
|
||||
|
|
|
@ -154,7 +154,7 @@ class RendererIntegrationTests(TestCase):
|
|||
|
||||
def test_default_renderer_serializes_content_on_accept_any(self):
|
||||
"""If the Accept header is set to */* the default renderer should serialize the response."""
|
||||
resp = self.client.get('/', HTTP_ACCEPT='*/*')
|
||||
resp = self.client.get('/', headers={"accept": '*/*'})
|
||||
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
@ -162,7 +162,7 @@ class RendererIntegrationTests(TestCase):
|
|||
def test_specified_renderer_serializes_content_default_case(self):
|
||||
"""If the Accept header is set the specified renderer should serialize the response.
|
||||
(In this case we check that works for the default renderer)"""
|
||||
resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type)
|
||||
resp = self.client.get('/', headers={"accept": RendererA.media_type})
|
||||
self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
@ -170,7 +170,7 @@ class RendererIntegrationTests(TestCase):
|
|||
def test_specified_renderer_serializes_content_non_default_case(self):
|
||||
"""If the Accept header is set the specified renderer should serialize the response.
|
||||
(In this case we check that works for a non-default renderer)"""
|
||||
resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type)
|
||||
resp = self.client.get('/', headers={"accept": RendererB.media_type})
|
||||
self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
@ -195,7 +195,7 @@ class RendererIntegrationTests(TestCase):
|
|||
"""If both a 'format' query and a matching Accept header specified,
|
||||
the renderer with the matching format attribute should serialize the response."""
|
||||
resp = self.client.get('/?format=%s' % RendererB.format,
|
||||
HTTP_ACCEPT=RendererB.media_type)
|
||||
headers={"accept": RendererB.media_type})
|
||||
self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8')
|
||||
self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEqual(resp.status_code, DUMMYSTATUS)
|
||||
|
|
|
@ -2,7 +2,6 @@ import itertools
|
|||
from io import BytesIO
|
||||
from unittest.mock import patch
|
||||
|
||||
import django
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import redirect
|
||||
|
@ -20,7 +19,7 @@ from rest_framework.test import (
|
|||
|
||||
@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'])
|
||||
def view(request):
|
||||
data = {'auth': request.META.get('HTTP_AUTHORIZATION', b'')}
|
||||
data = {'auth': request.headers.get('authorization', b'')}
|
||||
if request.user:
|
||||
data['user'] = request.user.username
|
||||
if request.auth:
|
||||
|
@ -316,7 +315,7 @@ class TestAPIRequestFactory(TestCase):
|
|||
data=None,
|
||||
content_type='application/json',
|
||||
)
|
||||
assert request.META['CONTENT_TYPE'] == 'application/json'
|
||||
assert request.headers['content-type'] == 'application/json'
|
||||
|
||||
|
||||
def check_urlpatterns(cls):
|
||||
|
@ -334,19 +333,11 @@ class TestUrlPatternTestCase(URLPatternsTestCase):
|
|||
super().setUpClass()
|
||||
assert urlpatterns is cls.urlpatterns
|
||||
|
||||
if django.VERSION > (4, 0):
|
||||
cls.addClassCleanup(
|
||||
check_urlpatterns,
|
||||
cls
|
||||
)
|
||||
|
||||
if django.VERSION < (4, 0):
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
assert urlpatterns is cls.urlpatterns
|
||||
super().tearDownClass()
|
||||
assert urlpatterns is not cls.urlpatterns
|
||||
|
||||
def test_urlpatterns(self):
|
||||
assert self.client.get('/').status_code == 200
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from collections import namedtuple
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import Resolver404, URLResolver, include, path, re_path
|
||||
from django.urls import Resolver404, URLResolver, include, path
|
||||
from django.urls.resolvers import RegexPattern
|
||||
|
||||
from rest_framework.test import APIRequestFactory
|
||||
|
@ -93,7 +93,7 @@ class FormatSuffixTests(TestCase):
|
|||
def test_format_suffix_django2_args(self):
|
||||
urlpatterns = [
|
||||
path('convtest/<int:pk>', dummy_view),
|
||||
re_path(r'^retest/(?P<pk>[0-9]+)$', dummy_view),
|
||||
path('retest/<int:pk>', dummy_view),
|
||||
]
|
||||
test_paths = [
|
||||
URLTestPath('/convtest/42', (), {'pk': 42}),
|
||||
|
@ -145,10 +145,10 @@ class FormatSuffixTests(TestCase):
|
|||
def test_included_urls_mixed(self):
|
||||
nested_patterns = [
|
||||
path('path/<int:child>', dummy_view),
|
||||
re_path(r'^re_path/(?P<child>[0-9]+)$', dummy_view)
|
||||
path('re_path/<int:child>', dummy_view)
|
||||
]
|
||||
urlpatterns = [
|
||||
re_path(r'^pre_path/(?P<parent>[0-9]+)/', include(nested_patterns), {'foo': 'bar'}),
|
||||
path('pre_path/<int:parent>/', include(nested_patterns), {'foo': 'bar'}),
|
||||
path('ppath/<int:parent>/', include(nested_patterns), {'foo': 'bar'}),
|
||||
]
|
||||
test_paths = [
|
||||
|
@ -185,7 +185,7 @@ class FormatSuffixTests(TestCase):
|
|||
|
||||
def test_allowed_formats_re_path(self):
|
||||
urlpatterns = [
|
||||
re_path(r'^test$', dummy_view),
|
||||
path('test', dummy_view),
|
||||
]
|
||||
self._test_allowed_formats(urlpatterns)
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ class TestURLReversing(URLPatternsTestCase, APITestCase):
|
|||
path('v1/', include((included, 'v1'), namespace='v1')),
|
||||
path('another/', dummy_view, name='another'),
|
||||
re_path(r'^(?P<version>[v1|v2]+)/another/$', dummy_view, name='another'),
|
||||
re_path(r'^(?P<foo>.+)/unversioned/$', dummy_view, name='unversioned'),
|
||||
path('<path:foo>/unversioned/', dummy_view, name='unversioned'),
|
||||
|
||||
]
|
||||
|
||||
|
|
10
tox.ini
10
tox.ini
|
@ -1,10 +1,13 @@
|
|||
[tox]
|
||||
; https://docs.djangoproject.com/en/stable/faq/install/#what-python-version-can-i-use-with-django
|
||||
envlist =
|
||||
{py36,py37,py38,py39}-django30
|
||||
{py36,py37,py38,py39}-django31
|
||||
{py36,py37,py38,py39,py310}-django32
|
||||
{py38,py39,py310}-{django40,django41,django42,djangomain}
|
||||
{py311}-{django41,django42,djangomain}
|
||||
{py36,py37,py38,py39}-django32
|
||||
{py38,py39}-{django40,django41,django42}
|
||||
{py310}-{django32,django42,django50,djangomain}
|
||||
{py311}-{django42,django50,djangomain}
|
||||
{py312}-{django42,django50,djangomain}
|
||||
base
|
||||
dist
|
||||
docs
|
||||
|
@ -22,6 +25,7 @@ deps =
|
|||
django40: Django>=4.0,<4.1
|
||||
django41: Django>=4.1,<4.2
|
||||
django42: Django>=4.2,<5.0
|
||||
django50: Django>=5.0,<5.1
|
||||
djangomain: https://github.com/django/django/archive/main.tar.gz
|
||||
-rrequirements/requirements-testing.txt
|
||||
-rrequirements/requirements-optionals.txt
|
||||
|
|
Loading…
Reference in New Issue
Block a user