Skip to content

Commit 083d8d0

Browse files
committed
cleaned up the MRO
any reference to ContextMixin has been deleted in favor of AsyncContextMixin this was done due to inheritance problems throughout the package a number of methods have been added to some of the classes to make this possible
1 parent 2c28096 commit 083d8d0

File tree

8 files changed

+218
-39
lines changed

8 files changed

+218
-39
lines changed

django_async_extensions/aviews/generic/base.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,28 @@
99
from django.utils.functional import classproperty
1010
from django.views.generic.base import (
1111
View,
12-
ContextMixin,
1312
TemplateResponseMixin,
1413
RedirectView,
1514
)
1615

1716
logger = logging.getLogger("django.request")
1817

1918

19+
class AsyncContextMixin:
20+
"""
21+
A default context mixin that passes the keyword arguments received by
22+
get_context_data() as the template context.
23+
"""
24+
25+
extra_context = None
26+
27+
async def get_context_data(self, **kwargs):
28+
kwargs.setdefault("view", self)
29+
if self.extra_context is not None:
30+
kwargs.update(self.extra_context)
31+
return kwargs
32+
33+
2034
class AsyncView(View):
2135
@classproperty
2236
def view_is_async(cls):
@@ -110,13 +124,13 @@ async def options(self, request, *args, **kwargs):
110124
return response
111125

112126

113-
class AsyncTemplateView(TemplateResponseMixin, ContextMixin, AsyncView):
127+
class AsyncTemplateView(TemplateResponseMixin, AsyncContextMixin, AsyncView):
114128
"""
115129
Render a template. Pass keyword arguments from the URLconf to the context.
116130
"""
117131

118132
async def get(self, request, *args, **kwargs):
119-
context = self.get_context_data(**kwargs)
133+
context = await self.get_context_data(**kwargs)
120134
return self.render_to_response(context)
121135

122136

django_async_extensions/aviews/generic/dates.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
from django.utils import timezone
77
from django.utils.translation import gettext as _
88
from django.views.generic.dates import (
9-
BaseDateListView,
10-
BaseYearArchiveView,
119
timezone_today,
1210
_date_from_string,
1311
YearMixin,
@@ -75,16 +73,31 @@ async def uses_datetime_field(self):
7573
return self._cached_uses_datetime
7674

7775

78-
class AsyncBaseDateListView(
79-
AsyncMultipleObjectMixin, AsyncDateMixin, AsyncView, BaseDateListView
80-
):
76+
class AsyncBaseDateListView(AsyncMultipleObjectMixin, AsyncDateMixin, AsyncView):
77+
"""Abstract base class for date-based views displaying a list of objects."""
78+
79+
allow_empty = False
80+
date_list_period = "year"
81+
8182
async def get(self, request, *args, **kwargs):
8283
self.date_list, self.object_list, extra_context = await self.get_dated_items()
8384
context = await self.get_context_data(
8485
object_list=self.object_list, date_list=self.date_list, **extra_context
8586
)
8687
return self.render_to_response(context)
8788

89+
async def get_dated_items(self):
90+
raise NotImplementedError(
91+
"An AsyncDateView must provide an implementation of get_dated_items()"
92+
)
93+
94+
def get_ordering(self):
95+
"""
96+
Return the field or fields to use for ordering the queryset; use the
97+
date field by default.
98+
"""
99+
return "-%s" % self.get_date_field() if self.ordering is None else self.ordering
100+
88101
async def get_dated_queryset(self, **lookup):
89102
"""
90103
Get a queryset properly filtered according to `allow_future` and any
@@ -121,10 +134,12 @@ async def get_dated_queryset(self, **lookup):
121134

122135
return qs
123136

124-
async def get_dated_items(self):
125-
raise NotImplementedError(
126-
"An AsyncDateView must provide an implementation of get_dated_items()"
127-
)
137+
def get_date_list_period(self):
138+
"""
139+
Get the aggregation period for the list of dates: 'year', 'month', or
140+
'day'.
141+
"""
142+
return self.date_list_period
128143

129144
async def get_date_list(self, queryset, date_type=None, ordering="ASC"):
130145
"""
@@ -181,9 +196,12 @@ class AsyncArchiveIndexView(
181196
template_name_suffix = "_archive"
182197

183198

184-
class AsyncBaseYearArchiveView(
185-
AsyncYearMixin, AsyncBaseDateListView, BaseYearArchiveView
186-
):
199+
class AsyncBaseYearArchiveView(AsyncYearMixin, AsyncBaseDateListView):
200+
"""List of objects published in a given year."""
201+
202+
date_list_period = "month"
203+
make_object_list = False
204+
187205
async def get_dated_items(self):
188206
"""Return (date_list, items, extra_context) for this request."""
189207
year = self.get_year()
@@ -216,6 +234,13 @@ async def get_dated_items(self):
216234
},
217235
)
218236

237+
def get_make_object_list(self):
238+
"""
239+
Return `True` if this view should contain the full list of objects in
240+
the given year.
241+
"""
242+
return self.make_object_list
243+
219244

220245
class AsyncYearArchiveView(
221246
MultipleObjectTemplateResponseMixin, AsyncBaseYearArchiveView

django_async_extensions/aviews/generic/detail.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
from django.core.exceptions import ImproperlyConfigured
2+
from django.db import models
23
from django.http import Http404
34
from django.views.generic.detail import (
4-
SingleObjectMixin,
55
SingleObjectTemplateResponseMixin,
66
)
77
from django.utils.translation import gettext as _
88

9-
from django_async_extensions.aviews.generic.base import AsyncView
9+
from django_async_extensions.aviews.generic.base import AsyncView, AsyncContextMixin
1010

1111

12-
class AsyncSingleObjectMixin(SingleObjectMixin):
12+
class AsyncSingleObjectMixin(AsyncContextMixin):
13+
"""
14+
Provide the ability to retrieve a single object for further manipulation.
15+
"""
16+
17+
model = None
18+
queryset = None
19+
slug_field = "slug"
20+
context_object_name = None
21+
slug_url_kwarg = "slug"
22+
pk_url_kwarg = "pk"
23+
query_pk_and_slug = False
24+
1325
async def get_object(self, queryset=None):
1426
"""
1527
Return the object the view is displaying.
@@ -70,6 +82,30 @@ async def get_queryset(self):
7082
)
7183
return self.queryset.all()
7284

85+
def get_slug_field(self):
86+
"""Get the name of a slug field to be used to look up by slug."""
87+
return self.slug_field
88+
89+
def get_context_object_name(self, obj):
90+
"""Get the name to use for the object."""
91+
if self.context_object_name:
92+
return self.context_object_name
93+
elif isinstance(obj, models.Model):
94+
return obj._meta.model_name
95+
else:
96+
return None
97+
98+
async def get_context_data(self, **kwargs):
99+
"""Insert the single object into the context dict."""
100+
context = {}
101+
if self.object:
102+
context["object"] = self.object
103+
context_object_name = self.get_context_object_name(self.object)
104+
if context_object_name:
105+
context[context_object_name] = self.object
106+
context.update(kwargs)
107+
return await super().get_context_data(**context)
108+
73109

74110
class AsyncBaseDetailView(AsyncSingleObjectMixin, AsyncView):
75111
"""
@@ -80,7 +116,7 @@ class AsyncBaseDetailView(AsyncSingleObjectMixin, AsyncView):
80116

81117
async def get(self, request, *args, **kwargs):
82118
self.object = await self.get_object()
83-
context = self.get_context_data(object=self.object)
119+
context = await self.get_context_data(object=self.object)
84120
return self.render_to_response(context)
85121

86122

django_async_extensions/aviews/generic/edit.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
from django.core.exceptions import ImproperlyConfigured
22
from django.forms import models as model_forms
33
from django.http import HttpResponseRedirect
4-
from django.views.generic.base import ContextMixin, TemplateResponseMixin
5-
from django.views.generic.edit import FormMixin
4+
from django.views.generic.base import TemplateResponseMixin
65

7-
from django_async_extensions.aviews.generic.base import AsyncView
6+
from django_async_extensions.aviews.generic.base import AsyncView, AsyncContextMixin
87
from django_async_extensions.aviews.generic.detail import (
98
AsyncSingleObjectMixin,
109
)
1110

1211

13-
class AsyncFormMixin(FormMixin):
12+
class AsyncFormMixin(AsyncContextMixin):
13+
"""Provide a way to show and handle a form in a request."""
14+
15+
initial = {}
16+
form_class = None
17+
success_url = None
18+
prefix = None
19+
20+
def get_initial(self):
21+
"""Return the initial data to use for forms on this view."""
22+
return self.initial.copy()
23+
24+
def get_prefix(self):
25+
"""Return the prefix to use for forms."""
26+
return self.prefix
27+
1428
async def get_form_class(self):
1529
"""Return the form class to use."""
1630
# this is async so subclasses can be of the same nature
@@ -22,6 +36,28 @@ async def get_form(self, form_class=None):
2236
form_class = await self.get_form_class()
2337
return form_class(**self.get_form_kwargs())
2438

39+
def get_form_kwargs(self):
40+
"""Return the keyword arguments for instantiating the form."""
41+
kwargs = {
42+
"initial": self.get_initial(),
43+
"prefix": self.get_prefix(),
44+
}
45+
46+
if self.request.method in ("POST", "PUT"):
47+
kwargs.update(
48+
{
49+
"data": self.request.POST,
50+
"files": self.request.FILES,
51+
}
52+
)
53+
return kwargs
54+
55+
def get_success_url(self):
56+
"""Return the URL to redirect to after processing a valid form."""
57+
if not self.success_url:
58+
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
59+
return str(self.success_url) # success_url may be lazy
60+
2561
async def form_valid(self, form):
2662
"""If the form is valid, redirect to the supplied URL."""
2763
return HttpResponseRedirect(self.get_success_url())
@@ -34,7 +70,7 @@ async def get_context_data(self, **kwargs):
3470
"""Insert the form into the context dict."""
3571
if "form" not in kwargs:
3672
kwargs["form"] = await self.get_form()
37-
return ContextMixin().get_context_data(**kwargs)
73+
return await super().get_context_data(**kwargs)
3874

3975

4076
class AsyncModelFormMixin(AsyncFormMixin, AsyncSingleObjectMixin):
@@ -72,10 +108,31 @@ async def get_form_class(self):
72108

73109
return model_forms.modelform_factory(model, fields=self.fields)
74110

111+
def get_form_kwargs(self):
112+
"""Return the keyword arguments for instantiating the form."""
113+
kwargs = super().get_form_kwargs()
114+
if hasattr(self, "object"):
115+
kwargs.update({"instance": self.object})
116+
return kwargs
117+
118+
def get_success_url(self):
119+
"""Return the URL to redirect to after processing a valid form."""
120+
if self.success_url:
121+
url = self.success_url.format(**self.object.__dict__)
122+
else:
123+
try:
124+
url = self.object.get_absolute_url()
125+
except AttributeError:
126+
raise ImproperlyConfigured(
127+
"No URL to redirect to. Either provide a url or define"
128+
" a get_absolute_url method on the Model."
129+
)
130+
return url
131+
75132
async def form_valid(self, form):
76133
"""If the form is valid, save the associated model."""
77134
self.object = await form.asave()
78-
return super().form_valid(form)
135+
return await super().form_valid(form)
79136

80137

81138
class AsyncProcessFormView(AsyncView):

0 commit comments

Comments
 (0)