I am programming an experiment with different treatment Groups. For Treatment Group 3 and 4, I want to know the name of the Participants by using a form field. For the Control Group and Treatment Group 1 - 2, the name is irrelevant and thus, there should not be a forms_field.
I ws already thinking of excluding the form field in HTML by using if loops, so if treatment = 3 or 4, display the field. However, I cannot proceed to the next page for the other Treatment Groups since the field shouldn't be blank. Inserting a blank=True is not really an option since Treatment Group 3 and 4 could just skip the name. I want to "force" Treatment Group 3 and 4 to give me their name.
My code is too long to post it here, so I'll just post the relevant passages:
Modelsview
class Subsession(BaseSubsession):
def creating_session(self):
treatmentgroup = itertools.cycle(['ControlGroup','one','two','three', 'four'])
for p in self.get_players():
p.treatmentgroup = next(treatmentgroup)
class Group(BaseGroup):
pass
class Player(BasePlayer):
name = models.StringField(label="Your Name:")
transcribed_text = models.LongStringField()
levenshtein_distance = models.IntegerField()
guthaben = models.CurrencyField(initial=c(0))
cumulative_guthaben = models.CurrencyField()
Pagesview
class Welcome(Page):
form_model = 'player'
def is_displayed(self):
return self.round_number == 1
class Introduction(Page):
form_model = 'player'
def is_displayed(self):
return self.round_number == 1
class Math(Page):
form_model = 'player'
form_fields = ['name']
def is_displayed(self):
return self.round_number == 1
def before_next_page(self):
if self.round_number == 1:
self.player.guthaben = self.player.guthaben + Constants.endowment
def vars_for_template(self):
return {
'treatmentgroup': self.player.treatmentgroup,
'image_path': 'trophy/{}.png'.format(self.round_number)
}
HTML-Page
{% extends "global/Page.html" %}
{% load otree static %}
{% block title %}
The math task
{% endblock %}
{% block content %}
<p align="justify"> In case you have any questions, please open the door and wait for the experimenter to come to your cubicle.
</p>
{% if treatmentgroup == 'four' or treatmentgroup == 'three' %}
{% formfields %}
{% endif %}
{% next_button %}
{% endblock %}
Thanks in advance!
You can modify a django.forms subclass by using its init method. Your code doesn't show how the form gets instantiated, but somewhere you would have
form = Form_class() # unbound and
form = Form_class( request.POST ) # bound
You would modify these to pass hide_name=True if you do not want the name field displayed.
The form itself would include the method
def __init__(self, *args, **kwargs):
self.hide_name = kwargs.pop('hide_name', False)
super().__init__(*args, **kwargs)
self.hide_name is accessible in any method of the form, and form.hide_name in a template that is rendering it. To avoid validation errors you can remove that that field from the form
...
super().__init__(*args, **kwargs)
if self.hide_name:
self.fields.pop('name')
Related
I want to add to the cart the actual product I'm in (product_detail.html).
So in the product_unit, is just needed to specify the quantity of the product.
Anyway I can't make the unit_product, automatically add the actual product I'm in.
forms.py
class Product_unitForm(forms.ModelForm):
class Meta:
model = Product_unit
fields = [
'product',
'quantity',
]
widgets = {'product': forms.HiddenInput()}
I hide the product from the template, because it is just the actual product, no need to specify.
views.py
def product_detail(request, id_category=None,id_product=None):
actual_product = Product.objects.get(id = id_product)
#Has an actual customer
#FORM
form_product_unit = Product_unitForm(request.POST or None)
form_product_unit.fields['product'] = actual_product # I try to add the product this way
if form_product_unit.is_valid():
instance_product_unit = form.save(commit=False)
instance_product_unit.product.save()
last_order = Order.objects.last()
is_buying = False
if(last_order.status == "en curso"):
is_buying = True
context = {
"Product" : actual_product,
"Is_buying" : is_buying,
#FORMS
"form_product_unit" : form_product_unit,
}
return render(request, "shopping/product_detail.html", context)
I want to manually from the views, add the product field of product_unit to the actual product it has (actual_product)
template
<img src="{{Product.image.url}}"/>
<h1>{{Product.title}}</h1>
<form method="POST" action="">{% csrf_token %}
{{ form_product_unit.as_p }}
<input type="submit" value="Add" />
</form>
In your views.py file I think you just need to make two changes
def product_detail(request, id_category=None,id_product=None):
actual_product = Product.objects.get(id = id_product)
form_product_unit = Product_unitForm(data=request.POST or None,
initial={'product': actual_product})
And also remove the line form_product_unit.fields['product'] = actual_product. You might need to play around with the initial dictionary a bit to get it to bind the correct value to the field but that's the general idea. The related section in the docs is https://docs.djangoproject.com/en/1.9/ref/forms/api/#dynamic-initial-values
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 playing around with a relatively straightforward implementation of Whoosh 2.6 and django-haystack 2.3.1 for a search of "contact" objects. However, an example search for "mary" only returns a small subset of my many "Mary" contacts. Here are the relevant files:
search_indexes.py
from django.db.models import Q
from haystack import indexes
from apps.contact.models import Contact
class ContactIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
full_name = indexes.CharField(model_attr='full_name', null=True)
email = indexes.CharField(model_attr='email', null=True)
phone = indexes.CharField(model_attr='phone', null=True)
content_auto = indexes.NgramField(use_template=True)
# several more indexed=False fields to avoid db hits later
def get_model(self):
return Contact
def index_queryset(self, **kwargs):
# one of full_name, email, phone has to be non-null
return self.get_model().objects.filter(
Q(full_name__isnull=False) | Q(email__isnull=False) | Q(phone__isnull=False))
def get_updated_field(self):
return 'date_modified'
contact_text.txt and contact_content_auto.txt (both the same)
{% if object.full_name %}{{ object.full_name }}{% else %}{% endif %}
{% if object.email %}{{ object.email }}{% else %}{% endif %}
{% if object.phone %}{{ object.phone }}{% else %}{% endif %}
views.py
def search(request):
sqs = SearchQuerySet()
form = SearchForm(request.POST, searchqueryset=sqs, load_all=False)
if form.is_valid():
return form.search()
Relevant notes:
some code has been omitted for simplicity
the templates have if statements in order to get rid of the word "None" in the search index
currently, all of my Contact objects have a valid full_name
there is no pattern to which "mary" results are returned and which aren't: select count(*) from contact_contact where lower(full_name) like
'%mary%'; returns 97 rows, while the search returns only 5.
Similarly, for 483 "john" contacts, the search returns 19.
using Python 2.7, Django 1.5 and Postgres 8.4
I have a content that I'd like to rate on multiple criteria.
Imagine this kind of model:
class Content(models.Model):
name = models.CharField(max_length=50)
class Criterion(models.Model):
name = models.CharField(max_length=50)
content = models.ForeignKey(Content)
class ContRate(models.Model):
user = models.ForeignKey(User, help_text="Who rated ?")
crit = models.ForeignKey(Criterion)
rate = models.DecimalField()
The user has a page displaying the content.
From this page, he can also rate the content on the criteria set
The rating will be done with Ajax.
Now I'm trying to implement the view & the template
view.py
#...
def viewcont(request, content_id):
"""The user can view a content & rate it"""
content = get_object_or_404(Content, pk=content_id)
RateFormSet = modelformset_factory(ContRate)
formset = RateFormSet(queryset=ContRate.objects.filter(content=content, user=request.user))
objs = {
'content': content,
'forms': formset,
}
return render_to_response('content/content_detail.html', objs
, context_instance=RequestContext(request)
)
#...
content_detail.html
<!-- ... -->
<div id="rating">
<ul>
{% for crit in content.crit_set.all %}
<li>
{{ crit }}
<div class="rateit"
data-rateit-value="the_actual_rating_if_already_there"
data-rateit-ispreset="true"
crit-id="{{ crit.id }}"></div>
</li>
{% endfor %}
</ul>
</div>
<!-- ... -->
Now how can I use the forms formset to display the actual rates ?
And how can I draw an empty form to be posted by Ajax from any clicked star ?
(I know the javascript/jQuery part)
Not sure what the point of the formset is here. The rates are all available via the criteria object, using the reverse foreign key to ContRate in exactly the same way as you've done from Criteria to Content.
To make this as efficient as possible, you probably want to get the relevant ratings in the view and bring them together into a single datastructure:
content = get_object_or_404(Content, pk=content_id)
criteria = content.criteria_set.all()
user_ratings = ContRate.objects.filter(content=content, user=request.user)
ratings_dict = dict((c.crit_id, c.rate) for c in user_ratings)
for crit in criteria:
crit.user_rating = ratings_dict.get(crit.id)
Now you can pass criteria directly to your template, and there you can iterate through it to show the user_rating for each one.
(Final point: "criteria" is plural, the singular is "criterion". :-)
I can set the help_text attribute on any form field, but is it possible to set help_text on the choices used for a RadioSelect()?
I'd looking for a clean way to show some help information under each radio button.
Below is the code for the model and the form, I can render the name attribute in a template with the label, input element and help text. I'd also like to be able to render membership_type attribute with a label ('Membership Type'), radio buttons ('open membership' and 'closed membership'), and help text associated to each radio element ('anyone can join this group' and 'only select members can join this group').
class Group(models.Model):
MEMBERSHIP_CHOICES = (
('O', 'Open membership'),
('C', 'Closed membership'),
)
name = models.CharField(max_length=255)
membership_type = models.CharField(max_length=1, choices=MEMBERSHIP_CHOICES, default="O")
class GroupForm(forms.ModelForm):
name = forms.CharField(label="Group name", help_text="Enter a name for your new group")
class Meta:
model = Group
widgets = { "membership_type": forms.RadioSelect }
#Rishabh is correct but I'll elaborate further as, at first glance, it doesn't appear to be the solution, although it is; or, at least, it can be kludged to get a useful effect without having to dive too deep into django forms.
The second element of the tuple is presented inside the "label" tag - so any 'inline elements' are permissible; for example:
The desired result
Or something like it
<ul>
<li><label for="id_ticket_0">
<input type="radio" id="id_ticket_0" value="PARTTIME" name="ticket">
<em>Part Time</em> Valid on Friday Night and Saturday Only
</label></li>
<li><label for="id_ticket_1">
<input type="radio" id="id_ticket_1" value="DAYTIME" name="ticket">
<em>Casual</em> Valid on Saturday Only
</label></li>
<li><label for="id_ticket_2">
<input type="radio" id="id_ticket_2" value="EARLYBIRD" name="ticket">
<em>Early Bird</em> Valid on Friday, Saturday, and Sunday. $15 discount for booking before 1am January 3rd, 2011
</label></li>
</ul>
The simple example
The trick is to "mark_safe" the content of the description then stuff whatever you need into:
from django.utils.safestring import mark_safe
choices = (
('1', mark_safe(u'<em>One</em> | This is the first option. It is awesome')),
('2', mark_safe(u'<em>Two</em> | This is the second option. Good too.'))
)
The complex example
So in this example we:
assemble the choices into a list (any iterable structure will do)
pass the structure to the form's init to create our radio options on the fly
use a comprehension list to create an extended description for each radio option
The data structure:
Tickets are my own classes and they have attributes:
tickets.code - as in a ticket code
label - a pithy short description
help - a longer description
But more about that later. First lets create some instances:
from mymodule import ticket
# so lets create a few
fulltime = ticket('FULLTIME',160,'Full Time',
"Valid Monday to Friday inclusive")
parttime = ticket('PARTTIME',110,'Full Time',
"Valid outside of business hours only")
daytime = ticket('DAYTIME',70,'Day Time',
"Valid only on weekends and public holidays")
# and put them together in a list any way you like
available_tickets = [fulltime, parttime, daytime]
# now create the form
OrderForm(tickets=available_tickets)
That probably happened in your view code. Now to see what happens in the form
class OrderForm(ModelForm):
def __init__(self, *args, **kwargs):
self.tickets = kwargs.pop('tickets')
super(OrderForm, self).__init__(*args, **kwargs)
choices = [(t.code, mark_safe(u'<em>%s</em> %s' % (t.label, t.help)))
for t in self.tickets]
self.fields['ticket'] = forms.ChoiceField(
choices = choices,
widget = forms.RadioSelect()
)
For others coming across this 10+ years later, I was able to do this by creating a custom widget that overrides the default template for radio buttons, and passing custom attributes to it.
# Custom widget
class RadioSelectHelpTextWidget(forms.RadioSelect):
option_template_name = 'custom_templates/forms/radio_option_help_text.html'
# Form class that calls widgets, passed custom attributes to widget
class TemplateCreateForm(ModelForm):
created_by = forms.ModelChoiceField(required=False,queryset=User.objects.none())
class Meta:
model = Template
fields = ['name', 'type', 'created_by']
widgets = {
'name': forms.TextInput(attrs={'class': 'input'}),
'type': RadioSelectHelpTextWidget(
attrs={
'help_text': {
'custom': 'This is custom help text.',
'html': 'This is help text for html.'
}
}
)
}
Template for custom radio buttons (custom_templates/forms/radio_option_help_text.html)
{% load custom_template_filters %}
{% if widget.wrap_label %}
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>
{% endif %}
{% include "django/forms/widgets/input.html" %}
{% if widget.wrap_label %}
{{ widget.label }}
{% if widget.attrs.help_text %}
{% if widget.value in widget.attrs.help_text %}
<p class="is-size-7">
{{ widget.attrs.help_text|dict_get:widget.value }}
</p>
{% endif %}
{% endif %}
</label>
{% endif %}
The result:
Assuming you're using RadioSelect as a widget for forms.ChoiceField, you can do something like:
choices = (('1', 'First help_text here'),
('2', 'Second help_text here'),
('3', 'Third help_text here'),
)
class MyForm(forms.Form):
...
choice = forms.ChoiceField(widget = RadioSelect, choices = choices)
This isn't a strict use of help_text but it should get the job done in most cases.