I'm having my app on heroku.
I use timeme js to record user's active time spend on the page, and use a hidden form to store the value into the database. Additionally, I'm using otree package to write my app.
The following is the code I use:
models.py
class Active(ExtraModel):
active_duration = models.FloatField()
active_record = models.LongStringField()
views.py/pages.py in otree
class intro_instructions(Page):
def before_next_page(self):
data = self.form.data
p = self.player
active = Active.objects.create(active_duration=data['active_duration'],
active_record=data['active_record'])
active.save()
html
<form method="post" role="form" id="form" autocomplete="off">{% csrf_token %}
<input type="text" name="active_record" id="id_active_record" style="display: none" >
<input type="number" step='any' name="active_duration" id="id_active_duration" style="display: none">
</form>
The error
ValueError
could not convert string to float: ''
{
"csrfmiddlewaretoken": "vVhVRLrAUokmiNGRpzCaP78bnTOQyowf5VMfIDgKGglWGuEyQAU2wooWjQzXuBgD",
"active_duration": "",
"active_record": ""
}
Is it because active_duration is empty? Will it help if I set blank=true, null=true for the forms?
I was assuming that every use would have value for the input. Also any ideas about why the input is empty? Could it be that the user used scripts to skip the page without visible fields? From the sentry error message, this happened to one user twice.
It is because you cannot cast an empty string to a float. You can do this instead though:
active_duration=data.get('active_duration') or "0"
This will give you a "0" if data["active_duration"] is empty, False, or None.
>>> data = {"foo": ""}
>>> data.get("foo") or "bar"
'bar'
Related
I am working on a project right now and am having trouble figuring out something.
I have created a table in MySQL with the main input from the user being a "message" and a "creator"
What I am trying to do is have it so on the "Create message form", if a user wishes to not input their name, they can simply get a checkbox and "anonymous" would be passed through as "creator".
I havent been able to figure out how to get the "anonymous" input to get pass through.
If i type I leave the "creator" field empty and have the checkbox checked, my validations get called and says field cannot be empty, and if i remove the validations then is passes through but in the DB, the creator field is set as empty.
My next approach was to have the creator have a default of "anonymous"
in MySQL, i ran this "ALTER TABLE messages ALTER creator SET DEFAULT 'Anonymous';" and again it would call validations, and with no validations, it still enters the data as empty.
My next attempt is to maybe perhaps write an if statement in my validations, but not entirely sure how to write it out.
Hopefully this question makes sense...
UPDATE: I have since created an If statement that handles the "anonymous" checkbox. When no name is entered into the text input field, and the checkbox is checked, then "Anonymous" gets pushed into MySQL DB. NOW the issue is if I enter a name and the checkbox is unchecked, I receive a key error
KeyError: 'creator2'
I believe it has something to do with my If statement and have had no success in fixing it...
this is my form on the user side
<form action="/messages/create" method="post">
<div>
<label for="message">Enter Message</label>
<textarea name="message" id="message" cols="30" rows="10"></textarea>
</div>
<div>
<label for="creator">From</label>
<!-- <small class="form-text text-muted"></small> -->
<input type="text" name="creator1">
<input type="checkbox" id="Anonymous" name="creator2" >Anonymous</input>
</div>
<input type="submit" value="Create">
</form>
this is my controller to handle the form with the bugged if statement.
#app.route("/messages/create", methods = ["POST"])
def create_message():
data = request.form.to_dict()
if data["creator2"]:
data["creator"] = "Anonymous"
else:
data["creator"] = data["creator1"]
if Message.message_validator(data):
query_data = {
"message": data["message"],
"creator": data["creator"]
}
message_id = Message.create_message(query_data)
return redirect("/")
return redirect("/messages/new")
Okay I think I figured out why that happens, the data of a checkbox gets sent only if the checkbox is checked at the moment of the submission, that means when you submit the form without it being checked, you pass no data about that checkbox, like if it was not there at all, so when you parse the submitted form to a dictionary, you have no entry for the checkbox, thus raising the KeyError excpetion.
One fix is to check if the key is present in the dictionary.
#app.route("/messages/create", methods = ["POST"])
def create_message():
data = request.form.to_dict()
if "creator2" in data:
data["creator"] = "Anonymous"
else:
data["creator"] = data["creator1"]
if Message.message_validator(data):
query_data = {
"message": data["message"],
"creator": data["creator"]
}
message_id = Message.create_message(query_data)
return redirect("/")
return redirect("/messages/new")
I'd also suggest to have a value in the checkbox and to use equal names for name and if to avoid errors
<input type="checkbox" id="Anonymous" name="Anonymous" value="Anonymous">Anonymous</input>
First thing, as far as I know in HTML it's better to have the same value for the fields 'id' and 'name'. It can be error prone.
<input type="checkbox" id="anonymous" name="anonymous" >Anonymous</input>
Next thing I would link the anonymous checkbox with the input as it should be disabled when user declares as anonymous.
In that way, you can just use onClick methods to substitue the input value and only watch its content.
<input type="checkbox" id="anonymous" name="anonymous" onclick='handleClick();'>Anonymous</input>
In a js manner you implement the 'handleClick()' method. Something like :
function handleClick() {
var chk=document.getElementById("anonymous").value;
// DO SOMETHING
}
Django is creating two records in MySQL instead of one.
I call a function via a link
<button class="btn btn-primary">Thats Me!</button>
The function itself is very straight forward. I take the variable via a request.get, create a new object, and finally save it. However, when I check the DB there are two records, not just one.
def markpresent(request, id):
new_attendance = attendance(clientid_id = id, date = datetime.datetime.now(), camp = 3)
new_attendance.save()
return render(request, 'clienttracker/markpresent.html', {
'client': id,
})
Model
class attendance(models.Model):
clientid = models.ForeignKey(newclients, on_delete=models.CASCADE)
date = models.DateField()
camp = models.CharField(max_length = 3, default=0)
Any help and direction would be appreciated.
SOLUTION BASED ON ANSWERS
<form action="{% url 'markpresent' %}" method="post">
{% csrf_token %}
<button type="submit" name="client" value="{{ c.id }}" class="btn btn-primary">Thats Me!</button>
</form>
def markpresent(request):
id = request.POST.get('client')
new_attendance = attendance(clientid_id = id, date = datetime.datetime.now(), camp = 3)
new_attendance.save()
return render(request, 'clienttracker/markpresent.html', {
'client': id,
})
Thanks
You should avoid modifying your database on a GET request. Various things could cause a duplicate request - for instance, a request for an asset or favicon being caught by the same URL pattern and routed to the same view - so you should always require a POST before adding an entry in your database.
Are you using Google Chrome? If yes, then Google Chrome has something like lazy loading. So if you will type your URL in Google Chrome, it will try to load site behind the scenes and if you will tap enter, then you will get this URL again. The same is when you're trying to go over anchor with a link. It's an edge case, but it happens. Try with firefox or disable that function.
I have use ImageField in my django model to have the image upload facility. ImageField uses the ClearableFileInput widget but it does not provide well formatted html markup that i can customize using CSS. Shown bellow is the html markup that rendered by ClearableFileInput.
<div class="form-group" id="div_id">
<label class="control-label " for="id_image">
Guide
</label>
<div class="controls ">
Currently:
de.png
<input type="checkbox" name="image_" id="image_">
<label for="image_te">
Clear
</label><br>
Change:
<input type="file" name="image_te" id="id_i" class="clearablefileinput">
</div>
</div>
Simply what i want to do is to add custom css classes to these elements and change the order as i want. It would be really great if someone can suggest me a solution to this.
Just make your own Input class and alter the render callable to whatever you want. As an example, here's one I made to add in a little avatar. It's quick and dirty, in that it's not DRY, but it serves a purpose:
class AvatarInput(ClearableFileInput):
'''renders the input file as an avatar image, and removes the 'currently' html'''
template_with_initial = u'%(initial)s %(clear_template)s<br />%(input_text)s: %(input)s'
def render(self, name, value, attrs=None):
substitutions = {
'input_text': self.input_text,
'clear_template': '',
'clear_checkbox_label': self.clear_checkbox_label,
}
template = u'%(input)s'
substitutions['input'] = super(AvatarInput, self).render(name, value, attrs)
if value and hasattr(value, "url"):
template = self.template_with_initial
substitutions['initial'] = (u'<img src="%s" width="60" height="60"></img>'
% (escape(value.url)))
if not self.is_required:
checkbox_name = self.clear_checkbox_name(name)
checkbox_id = self.clear_checkbox_id(checkbox_name)
substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
substitutions['clear_template'] = self.template_with_clear % substitutions
return mark_safe(template % substitutions)
Then just drop it into your form class Meta:
class Meta:
model = <your model>
widgets = {'your-field-name': AvatarInput()
i want to update fields using ajax:
models.py
class EmployerInfo(models.Model):
employer = models.ForeignKey(Employer, unique=True)
address=models.CharField(max_length=100, blank=True)
city=models.CharField(max_length=40, blank=True)
contactinfo.html
<form id="ajax-form">
<fieldset>
<legend>Contact Information</legend>
<label>Address:</label>
<input type="text" id="address" value="{{ empinfo.address }}" />
<label>City:</label>
<input type="text" id="city" value="{{ empinfo.city }}" /> <i class="icon-ok"></i>
</fieldset>
</form>
<script>
$(document).ready(function()
{
$("form#ajax-form").find(":input").change(function()
{
var field_name=$(this).attr("id");
var field_val=$(this).val();
var params ={ param1:field_name, param2:field_val };
$.ajax({ url: "/employer/contactinfo/save/",
dataType: "json",
data: params,
success: setResult
});
});
});
urls.py
.....other urls
url(r'^employer/contactinfo/save/$', 'save_employer_info_ajax'),
view.py
def save_employer_info_ajax(request):
emp=Employer.objects.get(user=request.user)
emp_info=EmployerInfo.objects.get(employer=emp)
f_name=request.GET['param1']
f_value=request.GET['param2']
return HttpResponse(json.dumps({"issuccess": 'yes'}), content_type="application/json")
f_name the name of the field i want to update. lets assume it be 'address'.
how can i access that attribute, (ie emp_info.address) so that i can update (ie emp_info.address=f_value) using emp_info.save() function.
is there any method available other than emp_info.address, so that i can access the field name using string (ie emp_info[f_name]=f_value ) or something??
You could just use getattr baked into python
attr_name = 'employer_id'
if getattr(employee, attr_name) == 3:
...
You can use the update method on the queryset object. It's a bit hacky since you are really only wanting to update a single object though.
def save_employer_info_ajax(request):
emp_info_query = EmployerInfo.objects.filter(employer__user=request.user)
update_dict = {request.GET['param1'] : request.GET['param2'] }
emp_info_query.update(**update_dict)
Notice that I'm using reverse lookup relations to access EmployerInfo based on the user field of the Employer model. Then you construct a dictionary to hold the keys and values you wish to update, and pass that to the update method of the queryset, not the model instance.
You should be using forms for data entry though. There should be validation on all fields that you're entering into your database. You can use dynamic forms to specify the fields you want included based on the values that you submit via ajax.
I got the following very basic template:
<html>
<head>
</head>
<body>
<div>
<!-- Using "for" to iterate through potential pages would prevent getting empty strings even if only one page is returned because the "page" is not equal the query, it is a subcomponent of the query -->
<div>{{ page.name }}</div>
<div>{{ page.leftText }}</div>
<div>{{ page.imageURL }}</div>
<div>{{ page.rightText }}</div>
</div>
</body>
</html>
And the very basic Model:
class Page(db.Model):
name = db.StringProperty(required=True)
leftText = db.TextProperty()
rightText = db.TextProperty()
imageURL = db.LinkProperty()
And the very basic Handlers:
class BaseRequestHandler(webapp.RequestHandler):
#######
class PageContentLoadRequestHandler(BaseRequestHandler):
def renderPage(self, values):
directory = os.path.dirname(__file__)
path = os.path.join(directory, 'templates', 'simple_page.html')
return template.render(path, values, True)
def get(self):
page = db.get('aghwc21vZWJlbHIKCxIEUGFnZRgBDA')
#alternative code
# page db.get(db.key(self.request.get('key')))
# The solution is to call/fetch the wanted object/query
data = page.get() # or ... = Page.gql("GQL CODE").fetch(1)
values = {'page': page}
template_name = "simple_page.html"
return self.response.out.write(self.renderPage(values))
The key is just randomly taken out of my storage, it is a real existing key of a filled entity.
They idea is to load a page content dynamically into the doc via AJAX, problem is, that this handler returns an empty template.
No ERRORS, 200 HTTP Code, key exists etc, etc, etc.
I am totally broken and a bit annoyed, by such problems, because I quiet do not know where the fault could be.
regards,
EDIT: Changing the template values to there correct names, I now get the following erro:
values = {'page': page, 'name': page.name,}
AttributeError: 'NoneType' object has no attribute 'name'
Your properties are called 'leftText', 'rightText', and 'imageURL', but you're trying to print out 'left_text', 'right_text' and 'image_url'. Django, in its infinite wisdom, simply returns an empty string when you try to access a property that doesn't exist, rather than throwing an exception.