I am trying to make a chart in Django using the jchart module and to be able to chose which rows i want to retrieve from the db using the variable hosp from my views.py to HomeTotalStudies() (below)
views.py
from charts.chartsclass import HomeTotalStudies
def home(request):
hosp = request.user.userprofile.hospital
chart = HomeTotalStudies()
return render .....
here is the /charts/chartsclass.py
from jchart import Chart
from jchart.config import (DataSet, Axes, rgba, ScaleLabel, Tick, Tooltips,
Legend, LegendLabel, Title, Hover, InteractionModes,
ElementLine, ElementPoint, ElementRectangle)
class HomeTotalStudies(Chart):
chart_type = 'pie'
responsive = False
def get_labels(self,**kwargs):
return ["CT","MG","RF","DX"]
def get_datasets(self,**kwargs):
modality = ['CT','MG','RF','DX']
count = []
if hosp=='somestring' #this is the variable i want to get from the views.py
studies = GeneralStudyModel.objects.all()
else:
studies = GeneralStudyModel.objects.filter(equipmentattributes__institution_name__exact=hosp)
for mod in modality:
cc = studies.objects.filter(modality_type__exact=mod).count()
count.append(cc)
data = [count[i] for i in range(len(count))]
colors = [
rgba(255,99,132,1),
rgba(54,162,235,1),
rgba(255,159,64,1),
rgba(255,206,86,1)
]
return [DataSet(label="My DataSet",
data=data,
borderWidth=1,
backgroundColor=colors,
borderColor=colors)]
So, my question is, how can I pass this variable hosp from the view to the chart class so that i can make the query to the db table GeneralStudyModel and i retrieve only the rows needed?
Any one has any suggestion / idea / solution?
Thanks
Yep.
You have two possibilites:
the hard way: you make an AJAX call that returns an array, and you populate this in JavaScript. This implies to make a JSON view that returns a JSON array
the easy way: you need to use class-based-view's to make modern application and in your class you override the method get_context_data()
like this:
class GeneralStudyResultsView(generic.TemplateView):
template_name='general_study_results.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(GeneralStudyResultsView, self).get_context_data(**kwargs)
# Create a variable you fill
context['my_big_sql'] = GeneralStudyModel.objects.all()
return context
And from there, in your template file (this is your template file, not a JavaScript file) general_study_results.html add something like:
<script>
var myData =
{% for row in my_big_sql %}
{{ row.column }}{% if not forloop.last %},{% endif %}
{% endfor %};
</script>
And then you have all your data in your HTML file ready to be show thanks to charts.js
Just add a suitable initialiser to the chart class as follows:
class HomeTotalStudies(Chart):
def __init__(self, hosp, *args, **kwargs):
self.hosp = hosp
super().__init__(*args, **kwargs)
def get_datasets(self,**kwargs):
modality = ['CT','MG','RF','DX']
count = []
if self.hosp=='somestring' #this is the variable i want to get from the views.py
studies = GeneralStudyModel.objects.all()
else:
studies = GeneralStudyModel.objects.filter(equipmentattributes__institution_name__exact=self.hosp)
...
then in your view:
from charts.chartsclass import HomeTotalStudies
def home(request):
hosp = request.user.userprofile.hospital
chart = HomeTotalStudies(hosp='whatever')
return render .....
Related
I have a model class named TemplateImages that holds the reference to images in my media folder.
I want to display the images in a loop on a html file.
Here is my models.py code:
class TemplateImages(models.Model):
image = models.ImageField(upload_to='template_images') # template_images/en/chronological/en-chronological-resume-1.png.
type = models.CharField(max_length=40) # e.g. chronological, functional, combination, coverletter
language_code = models.CharField(max_length=7, choices=languages.LANGUAGE_CHOICES, default='en') # en.
def __str__(self):
return '{}'.format(self.image)
Here is my views.py code:
def templates(request):
...
language_pref = request.user.userprofile.language_preference
chronological_images = core_models.TemplateImages('chronological', 'en')
...
return render(request, 'templates/templates.html', {
...
'chronological_images': chronological_images,
...
})
Here is a screen shot of my db:
On my html page, I have placed the following django for loop to display the 25 images on the page:
{% for c_image in chronological_images %}
{{c_image.image|file_url|safe}}"
{% endfor %}
I receive the following error message:
'TemplateImage' object is not iterable
I have searched google, but cannot locate any useful examples.
Can someone explain what I have done wrong?
You've simply constructed a TemplateImages object, not run a query. Try this:
chronological_images = TemplateImages.objects.filter(
type='chronological',
language_code='en',
)
You only constructed a new TemplateImages object that you did not save to the database, and of course that single object is not iterable. If you want to perform a query, you should use TemplateImages.objects.filter(), like:
def templates(request):
# ...
language_pref = request.user.userprofile.language_preference
chronological_images = core_models.TemplateImages.objects.filter(
type='chronological',
language_code='en'
)
# ...
return render(request, 'templates/templates.html', {
# ...
'chronological_images': chronological_images,
# ...
})
I have some values that I collected from users in a form, which are saved to the DB. I want to take these values, perform an operation on them in function, and then return them so that I can use them in them alongside the other context.
Here is my model:
#models.py file
class MyModel(models.Model):
varA = models.PositiveIntegerField()
varB = models.PositiveIntegerField()
varC = models.PositiveIntegerField()
And here is the outside function that I am using:
#makecalc.py
def MakeCalc(varA, varB, varC):
#simplified from what I am actually doing
varD = varA*varB**varC
varE = varA+varB+varC
return varD, varE
And here is the views.py file:
#views.py file
from .makecalcs import MakeCalc
class MySummary(DetailView):
model = MyModel
def get_vars(self):
varD, varE = MakeCalc(varA, varB, varC) #Am I passing this correctly?
return varD, varE #How do I send these to context?
And finally the html:
<p> you input: {{ MyModel.varA }}</p>
<p> you input:{{ MyModel.varB }}</p>
<p> you input:{{ MyModel.varC }}</p>
<p> I output:{{ varD }}</p> #how do I call this?
<p> you input:{{ varE }}</p> #how do I call this?
So, my question is in the view, how do I add varD, varE to context so that I can use it alongside varA, varB, and varC from the model?
Add the get_context_data() method to your view, like this:
def get_context_data(self, **kwargs):
context = {}
context['your_custom_var'] = 'your_custom_value'
return super(MySummary, self).get_context_data(**context)
I have two models
Room and RoomLog
each single Room object can have multiple RoomLog objects.
the purpose of it is that Room object has got an attribute: value and method save(). I create a new related RoomLog object every time a user saves changed attribute of any specific Room object and saves it.
RoomLog object has got value attribute and date attribute.
date is related to Room save method so it gives Room changed value save DATE.
My question is:
Q1: How to return all unique days from all RoomLog objects so I know when any save took place ?
Q2: Let's say we know how to return unique days. So, the question is: how to select any day from these unique days and display all Room objects values by chosen date ? I would like to return last saved value from chosen date for each Room object.
The way I tackle both questions at the moment (I am looking for more Pythonic, faster, better performing solutions) is:
I created Form in which I iterate through RoomLog objects:
class MyForm(forms.Form):
roomdates = []
roomextracted = []
for r in RoomLog.objects.all():
if r not in roomdates:
roomdates.append(r.update_date)
for i in roomdates:
if i not in roomextracted:
roomextracted.append(i)
ROOMDATA = [(r, r) for r in roomextracted]
my_choice_field = forms.ChoiceField(choices=ROOMDATA)
then I have a view to pass selected date to another view in which I filter Room.objects.all() by selected date:
def choices(request):
form = RoomLogChoices()
form.fields['choice'].choices = list()
testlist = []
for rl in RoomLog.objects.all():
if rl.update_date not in testlist:
testlist.append(rl.update_date)
for d in testlist:
form.fields['choice'].choices.append(
(
d,d
)
)
return render(request, 'prostats/choices.html', {'form':form})
next I have choicesform.html in which I select date from dropdown menu:
{% extends "base.html" %}
{% block content %}
<form action="" method="post" >
{% csrf_token %}
<ul>
{% for choice in form.my_choice_field.field.choices %}
<li>
<input type="radio" name="my_choice_field" value="{{choice.0}}"
{% if equal form.my_choice_field.data choice.0 %}
checked="checked"
{% endifequal %}/>
<label for="">{{choice.1}}</label>
</li>
{% endfor %}
</ul>
<input type="submit" value="Submit" />
</form>
{% endblock %}
and this is the view in which I handle POST data
class AllRoomsView(ListView):
template_name = 'prostats/roomsdetail.html'
queryset = Room.objects.all()
def get_context_data(self, **kwargs):
context = super(AllRoomsView, self).get_context_data(**kwargs)
context['rooms'] = Room.objects.all()
context['rlog'] = RoomLog.objects.all()
roomsdates = []
for r in context['rlog']:
if r not in roomsdates:
roomsdates.append(r.update_date)
roomextracted = []
for i in roomsdates:
if i not in roomextracted:
roomextracted.append(i)
context['roomextracted'] = roomextracted
choicedate = self.request.GET.get('choice')
if choicedate != None:
choosend = choicedate
else:
choosend = '2016-02-01'
#filtered rlogs
rlfilteredempty = []
for r in context['rooms']:
i = RoomLog.objects.filter(room=r.id, update_date__lte = choosend).order_by('-update_date')[:1]
if i:
rlfilteredempty.append(i[0])
else:
rlfilteredempty.append(r)
context['rlfiltered'] = rlfilteredempty
context['choicedate'] = self.request.GET.get('choice')
#context['roomfiltersettime'] = RoomLog.objects.filter(update_date__lte = choosend)
context['roomfiltersettime'] = RoomLog.objects.filter(update_date__lte = choosend)
rslice = []
for r in context['rooms']:
i = RoomLog.objects.filter(room=r.id, update_date__lte = choosend).order_by('-update_date')[:1]
if i:
for rsobject in i:
rs = (r.flat.flat_number,r.flat.block.block_name,r.room_name)
rl = rsobject.id
rv = rsobject.room_value
rslice.append((rs,rl,rv))
else:
rs = (r.flat.flat_number,r.flat.block.block_name,r.room_name)
r = r.id
rslice.append((rs,r))
context['rslice'] = rslice
So, all of it what I have done I feel is not very good and maybe somebody can point me with some good ideas on how to tackle this problem better ?
EDIT: update of the post with my Room and RoomLog models:
class Room(models.Model):
room_name = models.CharField(max_length= 10)
room_value = models.PositiveSmallIntegerField(default=0)
flat = models.ForeignKey(Flat)
created_date = models.DateField(auto_now_add= True)
created_time = models.TimeField(auto_now_add= True)
substage_name = models.CharField(max_length=50,default="")
def __init__(self, *args, **kwargs):
super(Room, self).__init__(*args, **kwargs)
self.value_original = self.room_value
def save(self, **kwargs):
with transaction.atomic():
response = super(Room, self).save(**kwargs)
if self.value_original != self.room_value:
room_log = RoomLog()
room_log.room = self
room_log.room_value = self.value_original
room_log.save()
return response
class RoomLog(models.Model):
room = models.ForeignKey(Room)
room_value = models.PositiveSmallIntegerField(default=0)
update_date = models.DateField(auto_now_add= True)
update_time = models.TimeField(auto_now_add= True)
To return all unique days, use distinct() on your created_date field. That will, of course, only work if created_date is actually a date and not a datetime value!
RoomLog.objects.all().distinct('created_date')
If your created value is a datetime, you need to make it a date first, using Django's func() and F() functions. That uses the DATE() SQL functions that may not work on all databases, but it does on Postgres and probably many others.
RoomLog.objects.all()\
.annotate(created_date=Func(F('created'), function='DATE'))\
.order_by('-created_date')\
.distinct('created_date')
Only a partial answer. The second question depends on the layout of your models that you didn't post.
I'm working on something like an online store. I'm making a form in which the customer buys an item, and she can choose how many of these item she would like to buy. But, on every item that she buys she needs to choose what its color would be. So there's a non-constant number of fields: If the customer buys 3 items, she should get 3 <select> boxes for choosing a color, if she buys 7 items, she should get 7 such <select> boxes.
I'll make the HTML form fields appear and disappear using JavaScript. But how do I deal with this on my Django form class? I see that form fields are class attributes, so I don't know how to deal with the fact that some form instance should have 3 color fields and some 7.
Any clue?
Jacob Kaplan-Moss has an extensive writeup on dynamic form fields:
http://jacobian.org/writing/dynamic-form-generation/
Essentially, you add more items to the form's self.fields dictionary during instantiation.
Here's another option: how about a formset?
Since your fields are all the same, that's precisely what formsets are used for.
The django admin uses FormSets + a bit of javascript to add arbitrary length inlines.
class ColorForm(forms.Form):
color = forms.ChoiceField(choices=(('blue', 'Blue'), ('red', 'Red')))
ColorFormSet = formset_factory(ColorForm, extra=0)
# we'll dynamically create the elements, no need for any forms
def myview(request):
if request.method == "POST":
formset = ColorFormSet(request.POST)
for form in formset.forms:
print "You've picked {0}".format(form.cleaned_data['color'])
else:
formset = ColorFormSet()
return render(request, 'template', {'formset': formset}))
JavaScript
<script>
$(function() {
// this is on click event just to demo.
// You would probably run this at page load or quantity change.
$("#generate_forms").click(function() {
// update total form count
quantity = $("[name=quantity]").val();
$("[name=form-TOTAL_FORMS]").val(quantity);
// copy the template and replace prefixes with the correct index
for (i=0;i<quantity;i++) {
// Note: Must use global replace here
html = $("#form_template").clone().html().replace(/__prefix_/g', i);
$("#forms").append(html);
};
})
})
</script>
Template
<form method="post">
{{ formset.management_form }}
<div style="display:none;" id="form_template">
{{ formset.empty_form.as_p }}
</div><!-- stores empty form for javascript -->
<div id="forms"></div><!-- where the generated forms go -->
</form>
<input type="text" name="quantity" value="6" />
<input type="submit" id="generate_forms" value="Generate Forms" />
you can do it like
def __init__(self, n, *args, **kwargs):
super(your_form, self).__init__(*args, **kwargs)
for i in range(0, n):
self.fields["field_name %d" % i] = forms.CharField()
and when you create form instance, you just do
forms = your_form(n)
it's just the basic idea, you can change the code to whatever your want. :D
The way I would do it is the following:
Create an "empty" class that inherits from froms.Form, like this:
class ItemsForm(forms.Form):
pass
Construct a dictionary of forms objects being the actual forms, whose composition would be dependent on the context (e.g. you can import them from an external module). For example:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
In views, you can use python native "type" function to dynamically generate a Form class with variable number of fields.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
Pass the content to the form and render it in the template:
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
The "content" is a dictionary of field values (e.g. even request.POST would do).
You can see my whole example explained here.
Another approach: Rather than breaking the normal field initialization flow, we can override fields with a mixin, return an OrderedDict of dynamic fields in generate_dynamic_fields which will be added whenever its set.
from collections import OrderedDict
class DynamicFormMixin:
_fields: OrderedDict = None
#property
def fields(self):
return self._fields
#fields.setter
def fields(self, value):
self._fields = value
self._fields.update(self.generate_dynamic_fields())
def generate_dynamic_fields(self):
return OrderedDict()
A simple example:
class ExampleForm(DynamicFormMixin, forms.Form):
instance = None
def __init__(self, instance = None, data=None, files=None, auto_id='id_%s', prefix=None, initial=None,
error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None,
use_required_attribute=None, renderer=None):
self.instance = instance
super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order,
use_required_attribute, renderer)
def generate_dynamic_fields(self):
dynamic_fields = OrderedDict()
instance = self.instance
dynamic_fields["dynamic_choices"] = forms.ChoiceField(label=_("Number of choices"),
choices=[(str(x), str(x)) for x in range(1, instance.number_of_choices + 1)],
initial=instance.initial_choice)
return dynamic_fields
I want to create a database in google app engine with the following properties
class Questions(db.Model):
title = db.StringProperty()
author = db.StringProperty()
text = db.TextProperty()
date = db.DateProperty(auto_now_add = True)
votes = db.IntegerProperty()
answers = db.StringListProperty()
tags = db.StringProperty()
The problem is that when I go to the dashboard and try to create an entity from there, answers property isn't there.
Is there any better way to have a list of strings, so I can manipulate them seperately?
Update:
When I try to update the entity and add something on the string list:
The link is localhost:9082/questions-4889528208719872
class QuestionPageHandler(BaseHandler):
def get(self, *a, **kw):
sURL = self.request.url.split("-")
question = Questions.get_by_id(long(sURL[-1]))
self.render_content("questionpage.html",question=question)
def post(self, *a, **kw):
answer = self.request.get("answer")
sURL = self.request.url.split("-")
question = Questions.get_by_id(long(sURL[-1]))
question.answers.append(answer)
question.put() **<---- I forgot to add this EDIT**
And then on the html I use this:
{% for answer in question.answers %}
<div class="well span7">
<p>{{answer}}</p>
</div>
{% endfor %}
But I have an empty page.
The solution:
def post(self, *a, **kw):
answer = self.request.get("answer")
sURL = self.request.url.split("-")
question = Questions.get_by_id(long(sURL[-1]))
question.answers.append(answer)
**question.put()** <-- add this
Link to NDB
Maybe try NDB which has list type properties
look at ->
ndb.StringProperty(repeated=True)
or
ndb.StructuredProperty(xxx,repeated=True)