Django templates not updating after database changes via admin - django

I am using Django v2.2 admin to change the information on my database but after I change it and refresh the page, the new data is not there, only the old data.
A fix for this if I restart the server, the templates can now fetch the new data that I input.
views.py
# template with context
class Home(TemplateView):
template = 'home.html'
context = { 'bar': Baby.objects.all() }
def get(self, request):
return render(request, self.template, self.context)
home.html
{% for foo in bar %}
{{ foo.name }}
{{ foo.cost }}
{% endfor %}
How I can get the new data by refreshing the page and not restarting the server?

As others mentioned, use get_context_data() method is good idea, because ContextMixin is parent class (not base class, but part of TemplateView's __mro__ Method Resolution Order) of TemplateView which is responsible to pass data from view to template. But, if you want to render template manually using get() method, You should hit on database on every GET request (in your case).
class Home(TemplateView):
template = 'home.html'
def get(self, request):
self.context = {'bar': Baby.objects.all()}
return render(request, self.template, self.context)
Your code does not work, because static variables are initialized only once. In your case context was static variable.
Hope, it helps you.

Can you please try this?
class Home(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bar'] = Baby.objects.all()
return context

Related

The "post" method in Django

I created three files:
2- view.py :
class AddTeamView(View):
def get (self, request):
form = TeamForm()
context = {'form': form}
return render(request, 'add_team.html', context)
1-forms.py:
class TeamForm(forms.Form):
name = forms.CharField( max_length='100')
details = forms.CharField(max_length='250')
3-add_team.html:
-here there is another file called "base.html"
{% extends 'base.html' %}
{% block title %}
add team
{% endblock %}
{% block content %}
<form action="/add_team/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% endblock %}
and i went to cmd and entered the server "python manage.py runserver"
it appeared on the browser:
"This page isn’t working
If the problem continues, contact the site owner.
HTTP ERROR 405"
A view can support methods like GET, POST, PUT, etc. given the corresponding method exists, so the view should have a .get(..), .post(..), .put(..), etc. function.
Here you only implemented a def get(self, request), and so POST requests are not allowed.
Based on the data you however show, this looks like the typical usecase of a CreateView [Django-doc]. The idea of these views is to encapsulate common scenario's such that by overriding a few attributes, one creates a view that is tailored towards a specific case, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = '/some/success_url'
The TeamForm should probably be a ModelForm, or at least a Form where you override the .save(..) function to properly save your data to the database, since right now, the form is not doing anything (well it receives the data, but after validation, it throws it away).
You might want to override the form_valid(..) function in case you do not want to redirect to the success_url. Furthermore it is very common that the success_url is resolved lazily from a given view name, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = reverse_lazy('view_name')
So, we don’t need to do a conditional to check if the request is a POST or if it’s a GET:
Your views.py:
from django.views.generic import View
class AddTeamView(View):
def post(self, request):
form = TeamForm(request.POST)
if form.is_valid():
new_tm = TeamModel(name=form.cleaned_data['name'], details=form.cleaned_data['details'])
new_tm.save()
return redirect('team_list')
return render(request, 'add_team.html', {'form': form})
def get(self, request):
form = TeamForm()
return render(request, 'add_team.html', {'form': form})
Hope this help you...

Django : rendering a TemplateView in another

Using Django, I have multiple template files (A, B and C) that could be rendered in the same TemplateView called GenericView.
A, B and C uses the same View (let's call it DynamicView), therefore I need to call the rendering method of this DynamicView from GenericView's get_context_data`.
Is there a way I can render easily DynamicView's template within GenericView's template?
EDIT : I am using class based view coding
EDIT : Added some code to make my question clearer :
Here is my GenericView :
class GenericView(DetailView):
model = SimpleModel
template_name = "template.html"
def get_context_data(self, **kwargs):
context = super(GenericView, self).get_context_data(**kwargs)
tests = DynamicModel.objects.filter(test=context['object'].pk)
context['tests'] = tests
print("tests : ", tests[0]) # each test contains a field called "template_path", I would like to instanciate a DynamicView so that I can include the rendered page in context
return context
class DynamicView(TemplateView):
template_name = "dummy.html"
model = DynamicModel
def render_to_response(self, context, **kwargs):
absolute_path = get_object_or_404(DynamicModel, pk=self.kwargs['pk'])
page = render(self.request, absolute_path, context, content_type=None, status=None, using=None) # here the page is rendered
You could override DynamicView's get_template_names method.
Before doing that though, what is being considered in making the decision on what template to render? It might be better to make 3 different views, use inheritance to let them share code and not rework any of that view routing.
Yes, it can be done with the Django template tag. You need to call render function inside of this tag and put it inside of needed template.
EDIT after code updates.
from django import template
from django.shortcuts import get_object_or_404
from myapp.models import DynamicModel
register = template.Library()
#register.inclusion_tag('dummy.html', takes_context=True)
def render_dynamic_model(context, **kwargs):
absolute_path = get_object_or_404(DynamicModel, pk=kwargs['pk'])
return {
'absolute_path': absolute_path,
}
And put it into template template.html
{% render_dynamic_model pk %}
This code like a general idea, if you need some variables from request, you can pass it in the template and update template tag code respectively.
{% render_dynamic_model pk request.user %}

Passing Parameters to Django CreateView

I am trying to implement an appointment-making application where users can create sessions that are associated with pre-existing classes. What I am trying to do is use a django CreateView to create a session without asking the user for an associated class, while under the hood assigning a class to the session. I am trying to do this by passing in the pk of the class in the url, so that I can look up the class within the CreateView and assign the class to the session.
What I can't figure out is how exactly to do this. I'm guessing that in the template I want to have something like <a href="{% url create_sessions %}?class={{ object.pk }}>Create Session</a> within a DetailView for the class, and a url in my urls.py file containing the line
url(r'^create-sessions?class=(\d+)/$', CreateSessionsView.as_view(), name = 'create_sessions'), but I'm pretty new to django and don't exactly understand where this parameter is sent to my CBV and how to make use of it.
My plan for saving the class to the session is by overriding form_valid in my CBV to be:
def form_valid(self, form):
form.instance.event = event
return super(CreateSessionsView, self).form_valid(form)
If this is blatantly incorrect please let me know, as well.
Thank you!
GET parameters (those after ?) are not part of the URL and aren't matched in urls.py: you would get that from the request.GET dict. But it's much better to make that parameter part of the URL itself, so it would have the format "/create-sessions/1/".
So the urlconf would be:
url(r'^create-sessions/(?P<class>\d+)/$', CreateSessionsView.as_view(), name='create_sessions')
and the link can now be:
Create Session
and now in form_valid you can do:
event = Event.objects.get(pk=self.kwargs['class'])
urls.py
path('submit/request/<str:tracking_id>', OrderCancellationRequest.as_view(), name="cancel_my_order"),
Template
<form method="POST">
{% csrf_token %}
{{form | crispy}}
<button class="btn" type="submit">Submit</button>
</form>
View
class MyView(CreateView):
template_name = 'submit_request.html'
form_class = MyForm
model = MyModel
def form_valid(self, form, **kwargs):
self.object = form.save(commit=False)
self.object.created_at = datetime.datetime.now()
self.object.created_for = self.kwargs.get('order_id')
self.object.submitted_by = self.request.user.email
super(MyView, self).form_valid(form)
return HttpResponse("iam submitted")
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['header_text'] = "My Form"
context['tracking_id'] = self.kwargs.get('order_id')
return context

Dynamically get template name in bound Django form

I am trying to render a Django contact form on any arbitrary page. I am doing it with a request context processor and a template include. This allows me to display the form fine anywhere I want. Then I have a special URL that accepts POST requests (on GET, I just redirect them). If the form is valid, I send an email, and redirect to a success page. On form invalid, I know to pass the form bound with errors, but...I don't know which template to specify because the form is an include and the parent template could be anywhere.
The only way to get something in a Django view is from the request. I can get the path, and with more work, probably the original view from where the POST came from, but that doesn't get me the template.
# urls.py
url(r'^services/$', 'website.views.services', name='services'),
url(r'^services/contact/$', 'website.views.services_contact', name='services_contact'),
url(r'^services/contact/done/$', 'website.views.services_contact_done', name='services_contact_done')
# views.py
class ServicesView(TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
class ServicesContactView(View):
def get(self, request, *args, **kwargs):
return redirect('services')
def post(self, request, *args, **kwargs):
form = ContactForm(request.POST)
if form.is_valid():
form.send_email()
return redirect('services_contact_done')
else:
return render(request, ????, {'contact_form': form})
services_contact = ServicesContactView.as_view()
# contact.html
<h2>Contact me</h2>
<p>Enter your email to receive your questionnaire</p>
<form action="{% url 'services_contact' %}" method="post">
{% csrf_token %}
{% if contact_form.non_field_errors %}
{{ contact_form.non_field_errors }}
{% endif %}
{{ contact_form.as_p }}
<button type="submit" name="submit">Send questionnaire</button>
</form>
# home.html
{% extends "base.html" %}
{% block content %}
<h1>{{ site.name }}</h1>
{% include "services/contact.html" %}
{% endblock %}
The typical Django form view is somewhat silent on form invalid in that its scenario is mostly similar to an unbound form, so it's all just render in the end. My scenario is different due to the template include.
You could set up a session variable every time you render a template and use it afterwards when you need it :
request.session['template']="nameOfTemplate"
.
return render(request, request.session.get('template', 'default.html'), {'contact_form': form})
I know it requires to write a line of code every time you render a template, but that's the best solution I could think of.
If anybody needs this answer, I figured it out on my own. It's possible, but a different approach is required. First, a request context processor is not appropriate for this situation. They're fairly dumb because they just get something once and stick it in the context. Their only advantage is their global nature.
My context processor:
def contact_form(request):
"""
Gets the contact form and adds it to the request context.
You almost certainly don't want to do this.
"""
form = ContactForm()
return {'contact_form': form}
The nature of forms is that they act differently after being processed by Django's validation machinery, specifically ContactForm() is an unbound form and will always be. You don't want to do this (unless you want a form that simply displays but doesn't work). The TEMPLATE_CONTEXT_PROCESSORS should be edited to remove this processor.
Now the burden on displaying the form is back on the view, which also means just about any view must be able to handle POST requests as well. This means that editing each view that wants a contact form is required, but we can use the power of class-based views and mixins to handle most of the repetition.
ServicesView remains almost the same as a TemplateView, except with a mixin that will handle the form. This way, the template name always remains the same (my original problem), but with additional form power.
class ServicesView(ContactMixin, TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
ContactMixin uses FormMixin, to create and display a form, and ProcessFormView to handle the GET and POST requests for the form. And because the form's nature changes with different kinds of requests (unsubmitted, submitted and invalid, submitted and valid), get_context_data needs to be updated with the correct form class instance. Lastly, we probably want to prefix (namespace) our form because it can can be used anywhere, and we want to avoid conflicts when another possible form can POST to the same view. Thus, the mixin is:
class ContactMixin(FormMixin, ProcessFormView):
form_class = ContactForm
success_url = reverse_lazy('contact_done')
def get_form_kwargs(self):
kwargs = super(ContactMixin, self).get_form_kwargs()
kwargs['prefix'] = 'contact'
return kwargs
def get_context_data(self, **kwargs):
context = super(ContactMixin, self).get_context_data(**kwargs)
form_class = self.get_form_class()
context['contact_form'] = self.get_form(form_class)
return context
def form_valid(self, form):
form.send_email()
return super(ContactMixin, self).form_valid(form)
The subtleties of self.get_form_class() were almost lost on me if it were not for an example in the docs (of what not to do, heh) and another StackOverflow answer, where I would've usually just said self.form_class, which ignores the processing of the form.
Now I simply add ContactMixin to any view and {% include "includes/contact.html" %} to any template.

Django Class Based Views with popup functionality

I wanted to ask if the following implementation is rational against Django rules of Views and Class Based views.
The scenario, I have implemented a mini administration section for users, not wanting to grant access to the general admin section of Django.
I am ok with the implementation and everything is working properly, recently I added also a small mixin to allow users to add related items the django admin way, the mixin is working properly (based on the original mixin of the Django admin interface). Just wanted to ask if this is a good approach or should I move to another implementation? (this is still a bit rough, made just for tests)
The mixin, we are actually checking against a query args, _popup, this is nearly the same way django admin checks for passed popups, this allows the form to load normally if working on it directly or as a popup when called through the parent form. partials/popup_reponse.html
class PopupMixin(object):
is_popup = False
popup_var = '_popup'
def get_context_data(self, **kwargs):
''' Add the _popup to the context, so we can propagade the templates'''
context = super(PopupMixin, self).get_context_data(**kwargs)
if self.popup_var in self.request.GET:
self.is_popup = True
context['is_popup'] = self.is_popup
return context
def form_valid(self, form):
if self.popup_var in self.request.POST:
self.is_popup = True
''' If this is a popup request, then we are working with a form
this means we save the form, and then override the default form_valid implementation
this allows us to inject newly created items in the form '''
if self.is_popup:
self.object = form.save()
return SimpleTemplateResponse('partials/popup_response.html', {
'value': self.object.id,
'obj': self.object
})
return super(PopupMixin, self).form_valid(form)
Then our create:
class TestCreateView(PopupMixin,CreateView):
form_class = TestForm
template_name = "add.html"
success_url = reverse_lazy('manager-list-tests')
This way in the view, I can make sure that first the base template does load only a bare form and not any other template parts, additionally I get to add a hidden field in the form:
{% if is_popup %}
<input type="hidden" name="_popup" value=1>
{% endif %}
Through this I override the default form_valid to respond with the same template as django admin uses:
<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
<script type="text/javascript">
opener.dismissAddAnotherPopup(window, "{{ value }}", "{{ obj }}");
</script>
</body>
</html>