I have the following event feed (using Django-ical)
class EventFeed(ICalFeed):
def items(self):
meeting_id = self.kwargs['meeting_id']
return Meeting.objects.get(id=meeting_id)
I am trying to pass the meeting_id through the URL conf like so:
url(r'^meetings/(?P<meeting_id>\d+)/$', EventFeed()),
However this returns 'EventFeed' object has no attribute 'kwargs'?
As Daniel points out in the comments, this is a syndication framework. Therefore we need to use get_object, and to filter not get on the Meeting object:
class EventFeed(ICalFeed):
def get_object(self, request, *args, **kwargs):
return int(kwargs['meeting_id'])
def items(self, meeting_id):
return Meeting.objects.filter(id=meeting_id)
Related
I have a Django application that uses a JSON API as its data source.
Here's a simplified example of use in one of my views.py:
class GroupsList(LoginRequiredMixin):
def get(self, request, **kwargs):
# Get file list and totals
try:
group_list = group_adapter.list() # makes an API call and ALSO populates a meta info class
except APIAccessForbidden:
return HttpResponseRedirect(reverse('logout'))
return render(request, 'groups/index.html', {
# can I make a mixin to add data here gained from the API call?
'group_list': group_list,
})
This line:
The group_adapter.list() call populates some meta information into another class, that's not related to the group_list itself. I'd like to pass that data to the template. Ordinarily I'd use a context_processor, but when the context processor is called, the API call hasn't been made yet. I could manually check the information and add it to the render() method, but then I'd need to do that in dozens of different views.
Potential Solution #1: Create a Mixin For It
Can I use a mixin here that adds this information to context AFTER the view code runs but BEFORE render passes information to the template?
In other words is there a way to do this:
class GroupsList(LoginRequiredMixin, AddMetaInfoToContextMixin):
and then create a mixin something like this?
class AddMetaInfoToContextMixin(ContextMixin):
def get_context_data(self, **kwargs):
# self.request
context = super().get_context_data(**kwargs)
context['global_meta_information'] = get_global_meta_information()
return context
Potential Solution #2: Make an overridden templateview
Commenter Melvyn pointed out that I can potentially subclass TemplateView and override get_context_data(), so would something like this work?
class TemplateViewWithMeta(TemplateView):
def get_context_data(self, *args, **kwargs):
context = super(Home. self).get_context_data(*args, **kwargs)
context['global_meta_information'] = get_global_meta_information()
return context
class GroupsList(LoginRequiredMixin, TemplateViewWithMeta):
[...]
The typical workflow for a Django generic TemplateView is:
get()
get_context_data()
render_to_response()
So in your case keeping with the spirit of generic views, you could do it like this:
from django.views import generic
class BaseRemoteApiView(generic.TemplateView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.group_list = None
def get(self, request, *args, **kwargs):
try:
self.group_list = group_adapter.list() # makes an API call and ALSO populates a meta info class
except APIAccessForbidden:
return HttpResponseRedirect(reverse('logout'))
return super().get(request, *args, **kwargs)
class RemoteApiContextMixin(generic.base.ContextMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["group_list"] = self.group_list
context["meta_information"] = get_global_meta_information()
return context
class ConcreteRemoteApiView(RemoteApiContextMixin, BaseRemoteApiView):
pass
Of course, you don't have to make 3 classes and can just combine the 3 into one - depends on how mixable you want to be.
I am new in Django and I am bit confused in Django apiview for custom method.
In ApiView, How can I create a custom method and How to call from axios.
For example
Here is my View
class TimeSheetAPIView(APIView):
#action(methods=['get'], detail=False)
def getbytsdate(self, request):
return Response({"timesheet":"hello from getbydate"})
def get(self,request,pk=None):
if pk:
timesheet=get_object_or_404(TimeSheet.objects.all(),pk=pk)
serializer = TimeSheetSerializer(timesheet)
return Response({serializer.data})
timesheet=TimeSheet.objects.all()
serializer = TimeSheetSerializer(timesheet,many=True)
return Response({"timesheet":serializer.data})
Here is my URL=>
url(r'^timesheets_ts/(?P<pk>\d+)/$', TimeSheetAPIView.as_view()),
url(r'^timesheets_ts/', TimeSheetAPIView.as_view()),
Normally my url would be like=>
api/timesheet_ts/
this one will get all of my records.
So my question is how can I setup URL for getbytsdate or getbyname or other some kind of custom get method? and how can I call?
I tried like this way=>
url(r'^timesheets_ts/getbytsdate/(?P<tsdate>[-\w]+)/$', TimeSheetAPIView.as_view()),
and I called like that
api/timesheets_ts/getbytsdate/?tsdate='test'
Its not work.
So please can u explain for the custom method in apiview and url setting?
In addition to your implementation, you just need to show your custom get request to your urls.py. Edit your urls.py as follows:
# urls.py
timesheet_getbytsdate_detail = TimeSheetAPIView.as_view({'get': 'getbytsdate'})
timesheet_detail = TimeSheetAPIView.as_view({'get': 'retrieve'})
urlpatterns = [
url(r'^timesheets_ts/getbytsdate/(?P<tsdate>[-\w]+)/$', getbytsdate_detail),
url(r'^timesheets_ts/(?P<pk>[0-9]+)/', timesheet_detail),
]
EDIT: You need to use the combination viewsets.GenericViewSet and mixins.RetrieveModelMixin instead of APIVewto get use of that:
class TimeSheetAPIView(viewsets.GenericViewSet, mixins.RetrieveModelMixin):
#action(methods=['get'], detail=False)
def getbytsdate(self, request):
return Response({"timesheet":"hello from getbydate"})
def retrieve(self, request, *args, **kwargs):
timesheet=self.get_object()
serializer = TimeSheetSerializer(timesheet)
return Response({serializer.data})
timesheet=TimeSheet.objects.all()
serializer = TimeSheetSerializer(timesheet,many=True)
return Response({"timesheet":serializer.data})
Using Django, I'm looking for a way to use one url patern (with slug) to query one model and if nothing is found query a second model. I'm using Class Based Views.
I am following this answer, and the next View is being called. But then I get the following error:
"Generic detail view must be called with either an object pk or a slug."
I can't figure out how to pass the slug to the next View.
My url:
url(r'^(?P<slug>[-\w]+)/$', SingleView.as_view(), name='singleview'),
My CBV's:
class SingleView(DetailView):
def dispatch(self, request, *args, **kwargs):
post_or_page_slug = kwargs.pop('slug')
if Page.objects.filter(slug=post_or_page_slug).count() != 0:
return PageDetailView.as_view()(request, *args, **kwargs)
elif Post.objects.filter(slug=post_or_page_slug).count() != 0:
return PostDetailView.as_view()(request, *args, **kwargs)
else:
raise Http404
class PageDetailView(DetailView):
model = Page
template_name = 'page-detail.html'
class PostDetailView(DetailView):
model = Post
template_name = 'post-detail.html'
The problem is that you are popping the slug, which removes it from kwargs. This means that the slug is not getting passed to the view.
You can change it to:
post_or_page_slug = kwargs.pop['slug']
I would usually discourage calling MyView.as_view(request, *args, **kwargs) inside another view. Class based views are intended to be extended by subclassing, not by calling them inside other views.
For the two views in your example, you could combine them into a single view by overriding get_object and get_template_names.
from django.http import Http404
class PageOrPostDetailView(DetailView):
def get_object(self):
for Model in [Page, Post]:
try:
object = Model.objects.get(slug=self.kwargs['slug'])
return object
except Model.DoesNotExist:
pass
raise Http404
def get_template_names(self):
if isinstance(self.object, Page):
return ['page-detail.html']
else:
return ['post-detail.html']
Let's say I have two models, Book and Page:
class Book(models.Model):
pass
class Page(models.Model):
book = models.ForeignKey(Book)
I need to delete a page and being redirected to the specific book that the page belonged to. To achieve this, I make a class based view to delete the page:
class PageDeleteView(DeleteView):
model = Page
def get_success_url(self, **kwargs):
return reverse_lazy('book_detail', self.book.pk)
The problem is that, since the object is deleted before get_success_url is called, this method fails, and I get a 404 error.
How could I do it?
Update:
Following the idea of #DrTyrsa, I have achieved it overriding the delete method, so the class would be as follows:
reverse_lazy = lambda name=None, *args : lazy(reverse, str)(name, args=args)
class PageDeleteView(DeleteView):
model = Page
def get_success_url(self, **kwargs):
return reverse_lazy('book_detail', self.book.pk)
def delete(self, request, *args, **kwargs):
self.book_pk = self.get_object().book.pk
return super(PageDeleteView, self).delete(request, *args, **kwargs)
Save object's pk before deleting. In __init__, for example. And name your URL patterns.
I've been experimenting with Django's Class Based Views and am trying to write a simple class based view that processes certain information in request so that the processed information can be used by the "handler" method.
I don't seem to have fully understood what the docs say and am unsure of whether this should be a Mixin, a generic view or something else. I'm thinking of making a class like this:
class MyNewGenericView(View):
redirect_on_error = 'home'
error_message = 'There was an error doing XYZ'
def dispatch(self, request, *args, **kwargs):
try:
self.process_information(request)
# self.process_information2(request)
# self.process_information3(request)
# etc...
except ValueError:
messages.error(request, self.error_message)
return redirect(self.redirect_on_error)
return super(MyNewGenericView, self).dispatch(request, *args, **kwargs)
def process_information(self, request):
# Use get/post information and process it using
# different models, APIs, etc.
self.useful_information1 = 'abc'
self.useful_information2 = 'xyz'
def get_extra_info(self):
# Get some extra information on something
return {'foo':'bar'}
This will allow someone to write a view like:
class MyViewDoesRealWork(MyNewGenericView):
def get(self, request, some_info):
return render(request, 'some_template.html',
{'info':self.useful_information1})
def post(self, request, some_info):
# Store some information, maybe using get_extra_info
return render(request, 'some_template.html',
{'info':self.useful_information1})
Is the above code the right way to go? Is there any simpler/better way of doing this? Will this prevent the above functionalities from being used in another generic view (e.g. a built-in generic view)?
Have a look at this. great example code. http://www.stereoplex.com/blog/get-and-post-handling-in-django-views
It seems I just asked a stupid question.
This can easily be achieved by making a class that processes that information:
class ProcessFooInformation(object):
def __init__(self, request):
self.request = request
#property
def bar(self):
baz = self.request.GET.get('baz', '')
# do something cool to baz and store it in foobar
return foobar
# etc...
Then using old style function views or new class-based views:
def my_view(request):
foo = ProcessFooInformation(request)
# use foo in whatever way and return a response
return render(request, 'foobar.html', {'foo':foo})
I also made this more efficient by using lazy evaluation of properties.
I adapted ideas from the lazy property evaluation recipe and the comments to write a wrapper:
def lazy_prop(func):
def wrap(self, *args, **kwargs):
if not func.__name__ in self.__dict__:
self.__dict__[func.__name__] = func(self, *args, **kwargs)
return self.__dict__[func.__name__]
return property(wrap)
This evaluates the value of the wrapped method only once per instance and uses a stored value on subsequent calls. This is useful if the property evaluates slowly.