Django rendering different templates in one class based view - html

I'm starting to use class based views for an application I'm creating but I'm nots sure how it works.
What I need is to have three different templates, and each template will show different information depending on a model field. My question is if there's a way to have only one class view that can render three different html templates with 3 different contexts, or if I need to create 3 different classes.
Using function based views, I would just do this:
# def humanResourcesView(request):
# context = {
# 'data' : Document.objects.all().filter(documentType='humanResources'),
# }
# return render(request, 'main/hr.html', context)
# #view to display training documents after click
# def trainingView(request):
# context = {
# 'data' : Document.objects.all().filter(documentType='training'),
# }
# return render(request, 'main/training.html', context)
# #view to display resource documents after click
# def reportsView(request):
# context = {
# 'data' : Document.objects.all().filter(documentType='reports')
# }
# return render(request, 'main/reports.html', context)
But I'm not sure how it works with class based views. Currently I have this, which renders and filters data correctly for one template, but I don't know how to do multiple templates. Do I need to create 3 different classes?
class DocumentView(View):
def get(self, request, *args, **kwargs):
reports = Document.objects.all().filter(documentType="reports")
context = {
'reports' : Document.objects.all().filter(documentType='reports')
}
return render(request, 'documents/reportsDocs.html', context)
Is there a way to only have one class, and pass a certain context depen

You would need to inherit from TemplateView
and override the get_template_names method to return your conditional based template.
And you would override get_context_date to fetch the data accordingly.
from django.views.generic import TemplateView
class DocumentView(TemplateView):
def get_template_names(self):
pass
def get_context_data(self, **kwargs):
pass

Related

SQLAlchemy 1.4 abstracting MetaData.reflect into function not returning MetaData object?

I have a class that acts as a PostgreSQL database interface. It has a number of methods which do things with the MetaData, such as get table names, drop tables, etc.
These methods keep calling the same two lines to set up MetaData. I am trying to tidy this up by abstracting this MetaData setup into its own function which is initiated when the class is instantiated, but this isn't working, as the function keeps returning NoneType instead of the MetaData instance.
Here is an example of the class, BEFORE adding the MetaData function:
class Db:
def __init__(self, config):
self.engine = create_async_engine(ENGINE, echo=True, future=True)
self.session = sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)
def get_table_names(self):
meta = MetaData()
meta.reflect(bind=sync_engine)
meta = self.meta()
return meta.tables.keys()
This works well, returns a list of table keys:
dict_keys(['user', 'images', 'session'])
When I try to shift the MetaData call into its own function like so:
class Db:
def __init__(self, config):
self.engine = create_async_engine(ENGINE, echo=True, future=True)
self.session = sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)
self.meta = self.get_metadata()
def get_metadata(self):
meta = MetaData()
return meta.reflect(bind=sync_engine)
def get_table_names(self):
return self.meta.tables.keys()
It returns this error:
in get_table_names return self.meta.tables.keys()
AttributeError: 'NoneType' object has no attribute 'tables'
How can I achieve this sort of functionality by calling self.meta() from within the various class methods?
Reflect alters the current metadata in-place. So you can just return the meta variable explicitly.
class Db:
# ...
def get_metadata(self):
meta = MetaData()
meta.reflect(bind=sync_engine)
return meta
# ...
Although it might be better to do this in a factory function, like def db_factory(config): and inject these things already prepped in the class constructor, def __init__(self, metadata, engine, session):. Just a thought.
Just wanted to post an answer, as with someone else's help I was able to solve this. The code should look like this:
class Db:
def __init__(self, config):
self.engine = create_async_engine(ENGINE, echo=True, future=True)
self.session = sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)
self._meta = MetaData()
#property
def meta(self):
self._meta.reflect(bind=sync_engine)
return self._meta
def get_table_names(self):
return self.meta.tables.keys()

Passing a context variable through a class in Django

how can i pass a context variable in a class. i know that i would use the render if i was showing my template from a function. and then i could just pass my context variable as part of the render. But how do i pass a context variable to html if i am using a class to show the template.
i have tried putting a function into my class but it has not worked.
views.py
class hithere(ListView):
model = Datadata
template_name = 'index.html'
def whatsup(request):
context = {}
context['my_string'] = "this is my sring"
return render(request, context)
Index.html
<h1> {{ my_string }} </h1>
You can override the .get_context_data(…) method [Django-doc]:
class hithere(ListView):
model = Datadata
template_name = 'index.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['my_string'] = 'this is my string'
return context
But perhaps more convenient is to define a method:
class hithere(ListView):
model = Datadata
template_name = 'index.html'
def my_string(self):
return 'this is my string'
and render this with:
{{ view.my_string }}
Note: normally a Django models, just like all classes in Python are given a name in PerlCase, not snake_case, so it should be: HitHereView instead of hithere.

How to store data from python/kivy app

I'd like to know how to store data from my app so I can review the data when I re-run the app.
e.g. I type some info in a TextInput and then when I click the submit button, the info is pasted in a label, so I close the app and when I reopen it the info should be appearing in the label. I know that there are sqlite3 and mysql but I don't know how to apply it into my python/kivy code.
Please anyone suggest me how that can be done.
If possible show with an example, it would be perfect.
Thanks in advance,
My py code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from random import shuffle
from kivy.core.window import Window
Window.clearcolor = [1, 1, 1, 1]
Window.size = (550, 650)
Builder.load_file('builder.kv')
class MainScreen(ScreenManager):
pass
class Menu(Screen):
pass
class Levels(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def on_pre_enter(self):
Window.bind(on_keyboard=self.voltar)
def voltar(self, window, key, *args):
if key == 27:
App.get_running_app().root.current = 'menu'
return True
def on_pre_leave(self):
Window.unbind(on_keyboard=self.voltar)
class LvLogos(Screen):
def on_pre_enter(self):
Window.bind(on_keyboard=self.voltar)
def voltar(self, window, key, *args):
if key == 27:
App.get_running_app().root.current = 'menu'
return True
def on_pre_leave(self):
Window.unbind(on_keyboard=self.voltar)
class Logo(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def shuffle(self):
letter = self.letters
self.s = shuffle(letter)
return letter
def on_pre_enter(self):
Window.bind(on_keyboard=self.voltar)
def voltar(self, window, key, *args):
if key == 27:
App.get_running_app().root.current = 'menu'
return True
def on_pre_leave(self):
Window.unbind(on_keyboard=self.voltar)
class LvShields(Screen):
pass
class Shield(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def shuffle(self):
letter = self.letters
self.s = shuffle(letter)
return letter
class QuizZApp(App):
def build(self):
self.icon = 'C:\\Users\\gusta\\PycharmProjects\\QuizzApp\\Images\\QuizzLogo.png'
return MainScreen()
if __name__ == '__main__':
QuizZApp().run()
Of course you can use sqlite3, but the simplest way to store basic data for kivy app like your exaple would be to use json file with kivy's own JsonStore class.
It has the benefit of allocating your file in the right place depending on the platform it is deployed on, you won't need to care where exactly.
Here is a simple example using get(), put() and exists() methods to store typed text from TextInput and load it on a Label. (You won't need to create the file itself, just initialize the object and write (put()) in it).
from kivy.uix.boxlayout import BoxLayout
from kivy.storage.jsonstore import JsonStore
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.properties import ObjectProperty
kv='''
RootWidget:
orientation: 'vertical'
BoxLayout:
TextInput:
id: txtinpt
Label:
id: lbl
text: root.stored_data.get('mydata')['text'] if root.stored_data.exists('mydata') else ''
Button:
size_hint_y: .3
text: 'Submit'
on_press:
root.stored_data.put('mydata', text=txtinpt.text)
lbl.text = txtinpt.text
'''
class RootWidget(BoxLayout):
stored_data = ObjectProperty(None)
def __init__(self, *args, **kwargs):
super(BoxLayout, self).__init__(*args, **kwargs)
self.stored_data = JsonStore('data.json')
runTouchApp(Builder.load_string(kv))
If you are new to Json, it is a file containing list of pairs, which's value by its own may be a new list of pair. pretty much like python's dict.
For kivy's JsonStore class, it assume you are working with a minimum of two levels, hence {"mydata": {"text": "What you have written last run"}}. It doesn't make much sense in this simple example to have a nested dict, but in general it will be exactly what you want for real applications, like if you wanted to take contacts data for multiple contacts, or you want to store various configurations for multiple widget for the app itself (in this case you may want to read (get()) the data prior of loading the widgets, probably in the App-class's build() method).
reference: https://kivy.org/docs/api-kivy.storage.html

Views.py processing the form data

My form.html
{{ form_field(task_form['execution_time']) }}
<input type="text" name="admin_time">
views.py
class CreateTaskView(LoginRequiredMixin, MyStaffUserRequiredMixin, generic.CreateView):
model = Task
form_class = TaskForm
template_name = 'tasks/form.html'
def get_context_data(self, *args, **kwargs):
ctx = super(CreateTaskView, self).get_context_data(*args, **kwargs)
ctx['task_form'] = ctx.get('form')
ctx['action'] = 'Add'
ctx['cancel_url'] = reverse('tasks.list')
return ctx
def form_valid(self, form):
form.save(self.request.user)
messages.success(self.request, _('Your task has been created.'))
return redirect('tasks.list')_url'] = reverse('tasks.list')
return ctx
When processing the form if admin_time has a value, then execution_time should be equal to admin_time.
How can I bring that about?
I want something like this- but it throws eror
def get_context_data(self, *args, **kwargs):
ctx = super(CreateTaskView, self).get_context_data(*args, **kwargs)
ctx['task_form'] = ctx.get('form')
if self.admin_time.is_valid():
task.execution_time=self.admin_time
else:
ctx['action'] = 'Add'
ctx['cancel_url'] = reverse('tasks.list')
return ctx
CreateView.get_context_data used to send additional context to your template (or override exist one) and it yields only when you have to render empty values request.GET
You can receive data on request.POST, so it yields methods in order (main of them) dispatch -> post -> form_valid or form_invalid -> redirect to success url. You can see there is no get_context_data so remove that weird code from get_context_data:
if self.admin_time.is_valid():
task.execution_time=self.admin_time
else:
I can see you override form_valid so if it runs - form already is valid and you can get "cleaned data" from it if you want perform some additional validation, put following code before form.save():
admin_time = form.cleaned_data['admin_time']
# check if it is not empty
if admin_time:
self.execution_time = admin_time
And you should not do this return ctx in your form_valid, because it never reach here after first return.
I also recommend you look at CreateView class implementation https://docs.djangoproject.com/en/1.7/ref/class-based-views/generic-editing/#createview and Django tutorials and docs if you want understand what you are doing https://docs.djangoproject.com/en/1.7/intro/tutorial01/ there ~ 6 tutorials, read and try it all and http://www.checkio.org/ for learning python. Because here we mainly don't loyal for such questions.

Django REST: ContentNotRenderedError when checking JSON object before rendering

I have a Django model, Note, which has a class-based view. It is supposed to return a JSON object upon the appropriate query.
Before returning the object, however, I would like to check that the user field in the note object matches the user currently logged in. (Users should not be able to access Note objects that are not their own.) To do this, I tried rewriting the get() method, calling on self.retrieve() to inspect the object before returning it:
class NoteDetail(generics.RetrieveUpdateDestroyAPIView):
model = Note
serializer_class = NoteSerializer
permission_classes = (permissions.IsAuthenticated,)
renderer_classes = (JSONRenderer,)
def get(self, request, *args, **kwargs):
current_user = User.objects.get(pk=self.request.user.id)
note = self.retrieve(request, *args, **kwargs)
if note.author is current_user:
return note
else:
raise PermissionDenied('Note does not belong to authenticated user.')(author=current_user)
However, this returns a ContentNotRenderedError when run: The response content must be rendered before it can be accessed.
Is there a way for me to check the object before returning it? Must I find a workaround?
One potential workaround is to redefine get_queryset(), rather than get() or get_object(). get_queryset() is the function that returns all objects of the relevant model; get_object() narrows down among these given the argument pk.
By overriding get_queryset(), you restrict the potential objects that get_object() can pick. Thus the set is already filtered at the time get() is called.
def get_queryset(self):
current_user = User.objects.get(pk=self.request.user.id)
# Must filter by author to prevent making everyone's notes public
queryset = Note.objects.filter(author=current_user)
return queryset