piston-django how many methods should a single handler contain - python

I've been building a handler class for each method I want to map to the url file. Is my approach correct or wrong? because I don't seem to find a way to map a resource to a method they all map to an entire class.
Regards,

The documentations seems very clear https://bitbucket.org/jespern/django-piston/wiki/Documentation#!resources
from piston.handler import BaseHandler
from myapp.models import Blogpost
class BlogpostHandler(BaseHandler):
allowed_methods = ('GET',)
model = Blogpost
def read(self, request, post_slug):
...
Piston lets you map resource to
models, and by doing so, it will do a
lot of the heavy lifting for you.
A resource can be just a class, but
usually you would want to define at
least 1 of 4 methods:
read is called on GET requests, and
should never modify data (idempotent.)
create is called on POST, and creates
new objects, and should return them
(or rc.CREATED.)
update is called on PUT, and should
update an existing product and return
them (or rc.ALL_OK.)
delete is called on DELETE, and should
delete an existing object. Should not
return anything, just rc.DELETED.
Also https://bitbucket.org/jespern/django-piston/wiki/Documentation#!mapping-urls
In urls.py:
from django.conf.urls.defaults import *
from piston.resource import Resource
from mysite.myapp.api.handlers import BlogpostHandler
blogpost_handler = Resource(BlogpostHandler)
urlpatterns = patterns('',
url(r'^blogpost/(?P<post_slug>[^/]+)/', blogpost_handler),
url(r'^blogposts/', blogpost_handler),
)

Related

Control requests to view and template output in django

This is a view for get all the records in the EducationalRecord model:
def all_education_resume(request):
RESUME_INFO['view'] = 'education'
educations_resume = EducationalRecord.objects.all().order_by('-created_date')
template = 'resumes/all_resume.html'
context = {'educations_resume': educations_resume, 'resume_info': RESUME_INFO}
return render(request, template, context)
Now, if I want to write exactly this view for other models (like job resumes, research resumes , etc.),
I must another view one separately.
My question is:
How can I get a view for all these requests, so first check the URL of
the request and then do the relevant query? How can I control URL
requests in my views?
My other question is exactly the same as my first question,with this difference:
control view that must render in specific template.In other words,in
second question the ratio between the template and the view is instead
of the ratio of the view to the url or how to create a template for
multiple views (for example, for a variety of database resume
resumes, I have a template) and then, depending on which view render,
the template output is different.
I have implemented these two issues as follows:
I wrote a view for each of request!
In each view, I set the value of RESUME_INFO['view'], and then I've checked it in a template page and specified the corresponding template.
What is the best solution to these two questions?
How can I get a view for all these requests, so first check the URL of the request and then do the relevant query? How can I control URL requests in my views?
You can access request.path, or you can let the url(..)s pass a parameter with kwargs that holds a reference to the model for example, but this is usually bad design. Typically if you use different models, you will likely have to order these different as well, filter these differently, render these differently, etc. If not, then this typically indicates that something is wrong with the modeling.
You can however make use of class-based views [Django-doc], to remove as much boilerplate as posssible. Your view looks like a ListView [Django-doc], by using such view, and patching where necessary, we can omit most of the "boilerplate" code:
# app/views.py
from django.views.generic.list import ListView
class MyBaseListView(ListView):
resume_info = None
template = 'resumes/all_resume.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['resume_info'] = {'view': self.resume_info}
return context
In the individual listviews, you then only need to specify the resume_info and the model or queryset to render it with the 'all_resume.html' template, for example:
# app/views.py
# ...
class EducationalResumeView(MyBaseListView):
queryset = EducationalRecord.objects.order_by('-created_date')
resume_info = 'education'
class OtherModelView(MyBaseListView):
model = OtherModel
resume_info = 'other_info'
So we can here use inheritance to define common things only once, and use it in multiple views. In case we need to change something in a specific view, we can override it at that level.
In the urls.py, you define such view with the .as_view() method [Django-doc]. For example:
# app/urls.py
from django.urls import path
from app.views import EducationalResumeView, OtherModelView
urlpatterns = [
path('education/', EducationalResumeView.as_view()),
path('other/', OtherModelView.as_view()),
]

Django: Global modification of readonly field appearance

I want to customize the field texture in django’s new “readonly mode”: E.g. foreign keys shall be displayed as links.
In general I identified the following options:
Implement custom fields for every model – results in code duplication
Reimplement django’s display_for_field method
Basically I could copy & paste the django.contrib.admin.utils module, insert my changes, override sys.modules['django.contrib.admin.utils'] = myutils but that's ugly because of maintainability in case of Django updates in the future.
So I decided to override only the display_for_fields method of django.contrib.admin.utils using the following approach to avoid duplication of Django code:
Override display_for_field function in django.contrib.admin.utils in settings.py:
from myapp.contrib.admin import utils
utils.override_method()
In myapp.utils.py:
from django.contrib.admin import utils
from django.contrib.admin.utils import *
def display_for_field_mod(value, field, empty_value_display):
if isinstance(field, models.ForeignKey) and value:
if field.related_model.__name__.lower() != 'user':
link_string = 'admin:myapp_' + field.related_model.__name__.lower() + '_change'
link = reverse(link_string, args=(value.id,))
return format_html('{}', link, value)
else:
return formats.localize(value)
else:
return display_for_field(value, field, empty_value_display)
def override_method():
utils.display_for_field = display_for_field_mod
But the problem is: display_for_field gets imported in django.contrib.admin.helpers using:
from django.contrib.admin.utils import (
display_for_field, [...]
)
So due to the scope of the imported funtion I cannot override this function from outside.
Do I miss some other obvious possibility? Is there a clean method to achieve this or is the only option to duplicate/modify django’s original code?
I came across a similar issue. If anyone's still looking for a solution, you just need to override the display_for_field in helpers, e.g.:
from django.contrib.admin import helpers, utils
def override_method():
helpers.display_for_field = display_for_field_mod
utils.display_for_field = display_for_field_mod
Only overriding helpers is sufficient, but it's a good idea to override utils as well, just in case.

How to implement method in Django REST?

Have the next Django REST question.
I have the view.
class MessageViewSet(viewsets.ModelViewSet):
serializer_class = MessageSerializer
queryset = Message.objects.filter(isread = False)
def mark_read():
queryset = Message.objects.update(isread=True)
return Response({'read':queryset})
And router in urls.py
router = SimpleRouter() router.register(r'api/get_messages', MessageViewSet)
urlpatterns = [
url(r'^$', MainView.as_view(), name='main'),
url(r'^', include(router.urls)) ]
Now i have 'get_messages' page which shows all list.
How can i implement a method which would change 'isread' value of model instanse from False to True, when I visit a 'mark_read' page?
As you can see, i tried to write method in the class. But when i'm trying to call it in urls in this way:
router.register(r'api/mark_read', MessageViewSet.mark_read),
Here comes an error.
assert queryset is not None, 'base_name argument not specified, and could ' \
AssertionError: base_name argument not specified, and could not automatically determine the name from the viewset, as it does not have a .queryset attribute.
Maybe i shouldnt use router, and rewrite view and urls in other way. If u know how to solve this problem, please answer. Thanks.
You can use detail_route or list_route decorators.
from rest_framework.decorators import list_route
class MessageViewSet(viewsets.ModelViewSet):
#list_route()
def mark_read(self, request):
queryset = Message.objects.update(isread=True)
return Response({'read':queryset})
With that mark_read method will be available at api/get_messages/mark_read. And you don't need to create separate router, just use one you created for MessageViewSet
docs reference
Since you are using a model viewset you can directly use put or patch rest method to send the desired value for the desired field as the data.
Ideally in rest get should not change model values. If you really want a different end point put the list_route or detail_route decorator on your mark_read method, and make them a valid call for only a put and/or patch call
from rest_framework.decorators import list_route
class MessageViewSet(viewsets.ModelViewSet):
#list_route(methods=['Patch', 'PUT'])
def mark_read(self, request):
queryset = Message.objects.update(isread=True)
return Response({'read':queryset})
Thanks to #ivan-semochkin and #Shaumux for replies. Advices were really helpful.
That is my route. I used detail_route instead of list_route.
#detail_route(methods=['get','put'], url_name='mark_read/')
def mark_read(self, request, pk=None):
queryset = Message.objects.filter(pk=pk).update(isread=True)
return Response({'read':queryset})
Now 'isread' value is changing wnen i visit 'mark_read' page.
Link: "api/get_messages/pk/mark_read"
Does anyone know, is it posslible to make links looking the next way:
"api/get_messages" - list, "api/mark_read/pk" - changing isread value.
Is it possible to create something like this? "api/mark_read?=pk"

Overwrite django view with custom context (Django 1.11, Viewflow)

I have a Django 1.11 project using Viewflow - https://github.com/viewflow/viewflow - that I've incorporated. It's been very helpful, but a lot of stuff is kind of "magic", and being my first serious Django project, I'm running into an issue I'm not sure of how to solve, or the best way.
I have a generic template that expects a lot of context. I have a function that adds this context to all of my views:
def add_general_context(context, MOC, MOC_enabled_fields = (), MOC_status = None):
context['MOC'] = MOC
context['current_date'] = timezone.now().strftime("%D")
context['MOC_form'] = forms.MOCForm(prefix="MOC_form", MOC_enabled_fields=MOC_enabled_fields, instance=MOC)
context['MOCAttachments'] = models.MOCAttachment.objects.filter(MOC=MOC)
context['MOCAttachment_form'] = forms.MOCAttachmentForm(prefix="MOCAttachment_form")
context['MOCApprovals'] = models.MOCApproval.objects.filter(MOC=MOC)
context['MOCTasks'] = models.MOCTask.objects.filter(MOC=MOC)
context['MOC_status'] = MOC_status
context['MOCConversation'] = models.MOCConversation.objects.filter(MOC=MOC)
# Add comments to the conversation
for conversation in context['MOCConversation']:
conversation.comments = models.MOCComment.objects.filter(conversation=conversation)
context['MOCComment_form'] = forms.MOCCommentForm(MOC=MOC)
context['MOCCommentReply_form'] = forms.MOCCommentReplyForm()
I basically need to add this context to a view that is inside viewflow - namely, AssignTaskView - https://github.com/viewflow/viewflow/blob/f50accb3cde5d53f1d4db0debf5936867712c3bd/viewflow/flow/views/task.py#L109
I've tried a few things to overwrite/add to the context, but none seem to work.
Attempt 1: Overwrite the URL and use extra_context (SO suggested this)
- The issue is that the urls are "magic", my urlpatterns is very simply:
from material.frontend import urls as frontend_urls
urlpatterns = [
url(r'^MOC/', include('MOC.urls')),
url(r'', include(frontend_urls)),
]
Overwriting the urls themselves was WAY over my head, I dug into it for a while, but it uses really generic functions to pull in modules, etc. I didn't have a clue how to really even attempt it.
Attempt 2: Overwrite the view itself and the get_context_data function
I think this would be possible, but it just doesn't seem to work. My attempts looked similar to this (the lastest one):
from viewflow.flow.views.task import AssignTaskView as CoreAssignTaskView
class AssignTaskView(CoreAssignTaskView):
def get_context_data(self, **kwargs):
context = super(AssignTaskView, self).get_context_data(**kwargs)
print("Did it work?")
return context
This is in my views.py - however, it simply doesn't run. I'm probably missing something, but I can't figure out how to actually force it to use my view instead of the one built in to viewflow.
I've successfully overwritten Viewflow's templates without issue, but overwriting anything else is beyond me. Any suggestions?
Yes you can actually override a view url by putting it on top of url_patterns
urlpatterns = [
url(
r'^/workflow/appname/flowname/(?P<process_pk>\d+)/taskname/(?P<task_pk>\d+)/assign/$',
YouCustomView.as_view(),
{'flow_task': FlowClass.taskname},
name="{}__assign".format(self.name)))
),
url(r'', include(frontend_urls)),
]
But it's simpler just to create a custom flow.View subclass and set you own Assign View
https://github.com/viewflow/viewflow/blob/master/viewflow/flow/nodes.py#L306
from viewflow import flow
class MyView(flow.View):
assign_view_class = MyAssignTaskView
flows.py:
class MyFlow(Flow):
...
taskname = MyView(UpdateProcessView).next(this.end)
That's how you can override any of the built-in views.
Viewflow designed to provide all knobs within your codebase. You can customize any behavior by subclassing Flow or flow Node classes.
The custom node example could be helpful
https://github.com/viewflow/viewflow/blob/master/demo/customnode/nodes.py

django: generic delete view

I want to build a generic delete view for my application. Basically I want to have the same behaviour as the standard django admin. E.g. I want to be able to delete different objects using the same view (and template).
I was looking on django docs, and looks like that DeleteViews are coupled with the models they are supposed to delete. E.g.
class AuthorDelete(DeleteView):
model = Author
success_url = reverse_lazy('author-list')
And I want to create something more generic, e.g.
class AnyDelete(DeleteView):
model = I want to have a list of models here
success_url = reverse_lazy('some-remaining-list')
The reason CBVs were invented were to solve problems like yours. As you have already written, to create your own delete view you just need to subclass DeleteView and change two properties. I find it very easy and do it all the time (the only non-dry work that I have to do is to hook it to urls.py).
In any case, if you really want to create something more generic (e.g only one view to delete every kind of model) then you'll need to use the content types framework. As you will see in the documentation, the content types framework can be used to work with objects of arbitrary models. So, in your case you can create a simple view that will get three parameters: app_label, model and pk of model to delete. And then you can implement it like this:
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
def generic_delete_view(request, app_label, pk):
if request.method == 'POST':
my_type = ContentType.objects.get(app_label=app_label, model=model)
get_object_or_404(my_type.model_class(), pk=pk).delete()
# here you must determine *where* to return to
# probably by adding a class method to your Models
Of course in your urls.py you have to hook this view so that it receives three parameters (and then call it like this /generic_delete/application/Model/3). Here's an example of how you could hook it in your urls.py:
urlpatterns = patterns('',
# ....
url(
r'^generic_delete/(?P<app_label>\w+)/(?P<model>\w+)/(?P<pk>\d+)$',
views.generic_delete_view,
name='generic_delete'
) ,
# ...
)
If you have a list of objects and want to get the app_label and model of each one in order to construct the generic-delete urls you can do something like this:
from django.core.urlresolvers import reverse
object = # ...
ct = ContentType.objects.get_for_model(object)
generic_delete_url = reverse('generic_delete', kwargs = {
app_label=ct.app_label,
model=ct.model,
pk=object.pk
})
# so generic_delete_url now will be something like /my_app/MyModel/42

Categories

Resources