Render form and model - html

I'm trying render one form and other model. Working only with {"convenio": miConvenio} I have a good result, but I don't know how can send the second value for render on html.
Thanks. Best regards.
def recibo(request):
miConvenio = Convenio()
if request.method=='POST':
miRecibo= FormularioRecibo(request.POST)
if miRecibo.is_valid():
infRecibo = miRecibo.cleaned_data
return render(request, "BieleGastosApp/recibo_impreso.html", {"form": miRecibo}, {"convenio": miConvenio})
else:
miRecibo = FormularioRecibo()
#print (miRecibo.mes)
return render(request, "BieleGastosApp/recibo.html", {"form": miRecibo})

You pass a dictionary with two (or more) items, so:
return render(
request,
'BieleGastosApp/recibo_impreso.html',
{'form': miRecibo, 'convenio': miConvenio} # ← dictionary with two items
)
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

Related

POST and GET requests to a client API URL using Django REST

I am kind of new to API.
I am working on a project where I can send POST and GET requests to different APIs. I just want to know how sort of the class in the view file should look like.
For example, I have a class that inherits generics.GenericAPIView. How do I send a get request to a specific URL or how do I save the data using the serializer to the database with post request?
class ArticelViewSet(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin):
serializer_class = ArticleSerializer_Modelserializers
queryset = Article.objects.all()
lookup_field = 'id'
def get(self, request, id=None):
if id:
return self.retrieve(request)
else:
return self.list(request)
def post(self, request):
return self.create(request) #return the created object

Django Rest Framework: what happened to my default Renderer?

I would like calls to /contacts/1.json to return json, 1.api to return browsableAPI, and calls with format=None aka /contacts/1/ to return a template where we call render_form. This way end-users can have pretty forms, and developers can use the .api format, and ajax/apps etc use .json. Seems like a common use case but something isn't clicking for me here in DRF...
Struggling with how DRF determines the Renderer used when no format is given. I found and then lost some info here on stack exchange that basically said to split the responses based on format. Adding the TemplateHTMLRenderer caused all sorts of pain. I had tried to split based on format but that is giving me JSON error below.
I don't understand the de facto way to define what renderer should be used. Especially when no format is provided. I mean, it "just works" when using Response(data). And I can get the TemplateHTMLRenderer to work but at the cost of having no default Renderer.
GET /contacts/1/ Gives the error:
<Contact: Contact object> is not JSON serializable
Using this code:
class ContactDetail(APIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
queryset = Contact.objects.all()
renderer_classes = (BrowsableAPIRenderer, JSONRenderer, TemplateHTMLRenderer,)
"""
Retrieve, update or delete a contact instance.
"""
def get_object(self, pk):
try:
return Contact.objects.get(pk=pk)
except Contact.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
contact = self.get_object(pk)
serializer = ContactSerializer(contact)
if format == 'json' or format == "api":
return Response(serializer.data)
else:
return Response({'contact': contact, 'serializer':serializer}, template_name="contact/contact_detail.html")
But GET /contacts/1.json , 1.api, or 1.html ALL give me the correct output. So it seems that I have created an issue with the content negotiation for the default i.e. format=None
I must be missing something fundamental. I have gone through the 2 tutorials and read the Renderers docs but I am unclear on what I messed up here as far as the default. I am NOT using the DEFAULT_RENDERERS in settings.py, didn't seem to make a difference if in default or inside the actual class as shown above.
Also if anyone knows a way to use TemplateHTMLRenderer without needing to switch on format value, I'm all ears.
EDIT: IF I use
if format == 'json' or format == "api" or format == None:
return Response(serializer.data)
else:
return Response({'contact': contact, 'serializer':serializer},
Then I am shown the browsable API by default. Unfortunately, what I want is the Template HTML view by default, which is set to show forms for end users. I would like to keep the .api format for developers.
TL; DR: Check the order of your renderers - they are tried in order of declaration until a content negotiation match or an error occurs.
Changing the line
renderer_classes = (BrowsableAPIRenderer, JSONRenderer, TemplateHTMLRenderer, )
to
renderer_classes = (TemplateHTMLRenderer, BrowsableAPIRenderer, JSONRenderer, )
Worked for me. I believe the reason is because the content negotiator starts at the first element in the renderer classes tuple when trying to find a renderer. When I have format==None, I'm thinking there is nothing else for DRF to go on, so it assumes I mean the "default" which seems to be the first in the tuple.
EDIT: So, as pointed out by #Ross in his answer, there is also a global setting in the settings.py for the project. If I remove my class level renderer_classes declaration and instead use this in settings.py
# ERROR
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.BrowsableAPIRenderer',
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
)
}
Then I get a (different) JSON error. However, as long as
'rest_framework.renderers.BrowsableAPIRenderer',
is not listed first, for example:
# SUCCESS, even though JSON renderer is checked first
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
So if we hit BrowsableAPIRenderer before we try TemplateHTMLRenderer then we get an error - whether or not we are relying on renderer_classes or DEFAULT_RENDERER_CLASSES. I imagine it passes through JSONRenderer gracefully but for whatever reason BrowsableAPIRenderer raises an exception.
So I have simplified my view code after analyzing this...
def get(self, request, pk, format=None):
contact = self.get_object(pk)
serializer = ContactSerializer(contact)
if format == None:
return Response({'contact': contact, 'serializer':serializer}, template_name="contact/contact_detail.html")
else:
return Response(serializer.data)
..which better reflects what I was originally trying to do anyway.
When I look at the source code, the priority seems to be the order of the renderers specified in the DEFAULT_RENDERER_CLASSES parameter in settings.py:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.TemplateHTMLRenderer',
)
}
So, if you specify a bunch of renderer classes, the first renderer that is valid will be selected based on if it is valid for the request given the .json/.api/.html extension and the Accept: header (not content-type, as I said in the comment on your question).

DjangoRestFramework - How to send JSON object and HTML Template to the front-end at the same time?

What I want is, if a user goes to this URL:
/user/22
then the front-end should render an HTML page for the user who's pk value is 22. This is my URLs.py:
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
urlpatterns = [
url(r'^user/(?P<pk>[0-9]+)$', views.UserPageView.as_view()),
url(r'^', include(router.urls)),
]
And this is UserPageView:
class UserPageView(generics.RetrieveAPIView):
queryset = User.objects.all()
renderer_classes = (TemplateHTMLRenderer,)
def get(self, request, *args, **kwargs):
self.user = self.get_object()
return Response({'user': self.user}, template_name='user.html')
And this is my UserViewSet:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsCreationOrAuthenticated, IsWatchOrOwnerOrReadOnly,)
Assuming 'user' is a JS object, I plan on using my DRF API by sending a get request using AngularJS to the following URL:
("/users/" + user.pk) // which leads to UserViewSet (which serializes the user object)
to get the information for that specific user. However, user is not a JSON object, it is a Django variable which I can use in the template using the Django template tags, like so: {{ user }}.
How do I get my DRF view to return JSON to the HTML template as well? What's the best way for me to do this? Thanks in advance!
I'm not exactly sure if there's any chance that it will work the way you're currently doing at, as the first met URL will always direct you to the UserPageView.
However, perhaps you should take a look at DRF's format_suffixes.
With an additional format you can specify if you want to get the JSON or HTML representation of the user object.

How to execute python code by django html button?

I want to execute my python code by onclick. I am getting result after running the server. My button is not working. Here is my code.
URL -
url(r'^index/$', index),
index.html-
<html>
<body>
<form action="/index/" method="GET">
<input type="submit" value="Click">
</form>
{{output}}
</body>
</html>
views.py -
from django.shortcuts import render, render_to_response
from pythoncode import mycode
def index(request):
if request.method=="GET":
py_obj=mycode.test_code(10)
py_obj.code()
return render(request, 'index.html', {"output":py_obj.a})
I created one more application to separate python code -
application name is python code and file name mycode
class test_code:
def __init__(self, a):
self.a=a
self.b=4
def code(self):
return self.a, self.b
please help me. I am new in Django. Thanks in advance
If you just want to click and display something on the fly on your page, you'll need JavaScript and AJAX. There is no need to create whole form just for one button. Remove your form completely, which closing tag is also wrong (read Brandon's comments).
You can use this snippet in your index.html:
<button id="myClickButton" type="button">Click</button>
<div id="myOutput"></div>
Now let's trigger something when clicking on the button:
$("#myClickButton").click(function() {
$.get("/output/", function(data) {
$("#myOutput").html(data);
}, "html");
});
The above code is jQuery. Please read the official documentation of jQuery. There is everything explained how to use the library.
Now let's go to your views.py.
def index(request):
return render(request, 'yourapp/index.html')
Remember to put your templates in a folder templates within your app. It should look like this:
--yourproject
|
|--yourapp
|----templates
|------yourapp
|--------index.html
Make another view in your views.py:
def output(request):
if request.is_ajax():
py_obj = mycode.test_code(10)
return render(request, 'yourapp/output.html', {'output': py_obj.a})
Your output.html can be like this:
<p>{{ output }}</p>
That's all. No header, no body, nothing. This code will be inserted per AJAX on the fly in index.html.
Now let's analyze your method code:
def code(self):
return self.a, self.b
Do you know what happens here? You can return only ONE value in a function. You think you're returning a and b as integers. Wrong! This method returns a tuple with two elements. This method will return (10, 4).
When you call this method in your index view it just returns this tuple, but you're not assigning it to a variable, so it will go with the wind. It's useless call.
I hope this gives you an idea how you can do it. If you don't want to use JavaScript (and AJAX) you can send your form per POST and make a distinction in your view:
def index(request):
if request.method == 'GET':
return render(request, 'yourapp/index.html', {'output': ''})
elif request.method == 'POST':
py_obj = mycode.test_code(10)
return render(request, 'yourapp/output.html', {'output': py_obj.a})
In this case you won't need the view output and output.html. You can use your index.html with the form inside.

Django Rest Framework: Retrieving object count from a model

Does anyone know how can I successfully retrieve the object count of a model, in JSON format, and how I need to configure my routing? I'm trying to achieve this using a APIView and returning a Response formatted by JSONRenderer.
UPDATE:
#api_view(['GET'])
#renderer_classes((JSONRenderer, JSONPRenderer))
def InfluenciasCountView(request, format=None):
influencia_count = Influencia.objects.count()
content = {'influencia_count': influencia_count}
return Response(content)
Here's the route I'm using:
url(r'^influencias/count/$', views.InfluenciasCountView, name='influencias-count')
Check out this snippet of code (the second one). If this does not suit your need, please add some of your code (for better understanding).
UPDATE
For routing, DRF offers a default router for each view. This means that you can have the following configuration in your urls.py: (using the example from the previous link)
url(r'^users/count/$', views. UserCountView.as_view(), name='users-count')
Then, when you access the URL your_base_url/users/count/ you will see something like {'user_count': 10}.
UPDATE 2
The entire code should look like this:
class UserCountView(APIView):
"""
A view that returns the count of active users.
"""
renderer_classes = (JSONRenderer, )
def get(self, request, format=None):
user_count = User.objects.count()
content = {'user_count': user_count}
return Response(content)
I am using routers from REST Framework to build my URLs. I tried above code but doesn't get it working. One of the problems is I cannot get /count/ into the router endpoints.
I checked DRF document (3.8.2) and found that there is a (new?) #action decorator (I was using 3.7.7 and it doesn't have it). So, here is my full solutions:
Upgrade DRF to 3.8.2 (or above) in requirements.txt (or PipFile if you using that).
Add a new action count method to the ModelViewSet
Update get_permissions to include the newly added action count
Here is my views.py:
from rest_framework.decorators import action
from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows recommend to be viewed or edited.
"""
model = Post
queryset = Post.objects.filter(is_active=True)
serializer_class = serializers.PostSerializer
filter_backends = (filters.SearchFilter, DjangoFilterBackend,)
search_fields = ('title', 'body',)
filter_fields = ('status', 'type')
def get_permissions(self):
if self.action in ('list', 'retrieve', 'create', 'count'):
return (AllowAny()),
if self.action in ('update', 'partial_update'):
return (IsAdminUser()),
return (IsAdminUser()),
#action(detail=False)
def count(self, request):
queryset = self.filter_queryset(self.get_queryset())
count = queryset.count()
content = {'count': count}
return Response(content)
To query count of posts: /api/posts/count/?format=json
To query count of published: /api/posts/count/?format=json&status=published
One of the important thing here is to use the queryset from filter_queryset(...), rather than Post.objects.all().
UPDATE
Since count is common, I created a mixin for that.
from rest_framework.decorators import action
from rest_framework.response import Response
class CountModelMixin(object):
"""
Count a queryset.
"""
#action(detail=False)
def count(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
content = {'count': queryset.count()}
return Response(content)
To use it, just add CountModelMixin to your ModelViewSet (also support nested ModelViewSet).
class PostViewSet(viewsets.ModelViewSet, CountModelMixin):
If you use permissions, also add 'count' to the list of granted action.