Getting False value for a key in self.data even when it is not present in the request - django

I have 2 radio buttons in the UI, one for setting a variable (ng-model) say selected as true (ng-value=true) and other one for setting it as false (ng-value=false). Now, when none of them is selected it results in the variable selected being absent from the outgoing request (as expected).
However, when that is dealt with Django Forms, the self.data dictionary in the clean() method gives False on accessing self.data.get('selected') / self.data['selected'] why is that so? Shouldn't it be None or at least give a key-error when it was not even present in the actual request?
Note that the variable 'selected' is actually a field in a Django Model with default=False, is that thing responsible for this behaviour? How can I circumvent this situation considering that altering the Django Model field isn't an option?

So I dealt with it the other day by checking for the selected key in the raw request.body. Now, since its a string, I had to parse it to a dict and then access the mentioned key using :
json.loads(request.body).get('selected')
In this way, if selected is not present at all when none of the radio buttons are selected, I get None. Similarly, if the radio button for ng-value=true is selected then I get True and vice-versa.

Related

Way to pass information from a GET parameter to a POST form in django?

I have a mailing list system in which a user can click a link to unsubscribe. This link contains the user's email in a GET parameter and the page it points to contains a short form to ask for feedback. This feedback needs to point to the email of the user who submitted it.
The way I tried to achieve this is:
take the email from the GET parameter
put it as initial value in a hidden field on the feedback form
retrieve it from form data when the form is sent
The problem is that if this hidden field is not disabled, the user can meddle with its value and dissimulate his own identity or even claim that the feedback came from another user. But if I set the field as disabled, the request.POST dictionary does not contain the field at all.
I also tried keeping the field enabled and checking for its presence in form.changed_data, but it seems to always be present there even if its value does not change.
This is the form class:
class UnsubscribeForm(forms.Form):
reason = forms.ChoiceField(choices=UnsubscribeFeedback.Reasons.choices)
comment = forms.CharField(widget=forms.Textarea, required=False)
user_email = forms.CharField(widget=forms.HiddenInput, required=False, disabled=False)
This is how I populate user_email in the view when the method is GET:
email = request.GET.get("email", "")
# ...
context["form"] = UnsubscribeForm(initial={"user_email": email})
Note that I also tried disabling the field manually after this line, as well as in the form's init method. The result is the same: if the field is disabled, the value does not get passed.
After setting the initial value, I print()ed it to make sure it was being set correctly, and it is. I also checked the page's source code, which showed the value correctly.
And this is how I check for the value in the POST part of the view, when the data-bound form is being received:
form = UnsubscribeForm(request.POST)
if form.is_valid(): # This passes whether I change the value or not.
if "user_email" in form.changed_data: # This too passes whether I change the value or not.
print("Changed!")
email = form.cleaned_data["user_email"] # This is "" if user_email is disabled, else the correct value.
I have no idea why the initial value I set is being ignored when the field is disabled. As far as I know, a disabled field passes the initial value over regardless of any changes, but here the initial value isn't being passed at all. And as I outlined above, I can't afford to keep this field editable by the user, even if it's hidden.
Django is version 3.0.3, if that matters.
Any solution? Is this a bug?
I found a solution to my problem, though it doesn't quite answer the question of why disabled fields ignore runtime initial values, so in a sense, the question is still open to answers.
In the original question, I crucially neglected to specify (in an effort to make the code minimal and reproducible) that the GET request that includes the user's email address also contains a token I generate with unpredictable data to verify that the email is authentic and corresponds to a subscribed user. In order to successfully meddle with the email, a user would also have to forge a valid token, which is unlikely (and not worth the effort) unless they have access to both my database and codebase (in which case I have worse problems than a feedback form).
I will simply keep the hidden field not disabled and also pass the token along, to verify that the address is indeed valid.

How to Avoid multi post request by one click?

I have a simple form which adds personal information of a family. sometime it saves two instance of a person just by one submit. Maybe my mouse has problem and and double clicks instead of one click (it has some problems). I think this is not possible and django accepts just one post request from an instance of a form and not more (maybe it accepts). what if may code has problem? if it is problem of my code, why it happens once a while?
house = get_object_or_404(House, id=code)
if request.method == 'POST':
form = ParentForm(request.POST)
if form.is_valid():
# save it if it's valid
parent = form.save(commit=False)
if parent.living == 0:
parent.in_family = 0
if not parent.guardian:
parent.save()
if parent.guardian and parent.in_family:
parent.save()
I use Django 1.8
Edit to clear: this is not the only view sometime saves twice. Maybe it is a bug in django
To solve this problem, first you need to create unique constraint on the corresponding database table. The real solution is based on the database schema. I don't know what fields (columns) you have in the parent table, you can start from add unique constraint to these two fields: child_id and parent_name.
The other problem is you need to prevent the second click. So basically you need to write some JavaScript code: it listens to the onClick event of the submit button. Once the button get clicked, the listener sets the disabled attribute to that button to prevent further clicks.

How to disable Sitecore WFFM custom fields caching

I have a custom field SomeCustomFieldType in Sitecore WFFM that shows on the left side of the Form Editor a property which is a dropdown with a list of choices.
[VisualProperty("Choose value", 99),
VisualCategory("Custom Properties"),
VisualFieldType(typeof(CountryBasedListChoiceField))]
public string ChosenValue { get; set; }
Instead of using the out-of-the-box ListChoiceField, I wrote my own CountryBasedListChoiceField, which, based on some condition, figures out which choice items need to be displayed in the property dropdown:
public CountryBasedListChoiceField()
: base(((object) HtmlTextWriterTag.Select).ToString())
{
string formID = Sitecore.Context.Request.GetQueryString("formid");
Language formLanguage = Language.Parse(Sitecore.Context.Request.GetQueryString("la"));
Item formItem = Sitecore.Context.ContentDatabase.GetItem(formID, formLanguage);
string countryName = SitecoreUtility.GetCurrentCountryISOCode(formItem, formLanguage);
string choicesRootPath = CountryConfigurationManager.GetCountrySite(countryName).Metadata.FormsStylesheetsItemPath;
Item choicesRootItem = Sitecore.Context.ContentDatabase.GetItem(choicesRootPath, formLanguage);
ChoicesRoot = choicesRootItem.ID.ToString();
}
It really doesn't matter how the above code works, since it works just fine.
The problem I am having is that the code above is executed only once, the very first time that the user opens a form in the form editor and clicks on a field that is of type SomeCustomFieldType.
After that, the list of choices to be displayed must be cached because this code is never being hit anymore. Even closing the browser and reopening it is not enough to clear that cache -- iisreset obviously clears that cache.
I would like to know if there is a way to stop this caching from happening.
Thanks in advance!
Firstly, you should check to ensure that it the rendering is not cached found here: '/sitecore/layout/Renderings/Modules/Web Forms for Marketers/Form' (the version of WFFM I have open right now it is NOT the default). Also check to ensure that any renderings it is contained within are also not cached.
Looking at the forms code, the form item and field containers are constructed every time. I also checked the .ascx that is called and it has no default output cache. You can start by looking deeper in Sitecore.Forms.Core.dll at the class 'Sitecore.Form.Web.UI.Controls.SitecoreSimpleFormAscx', but I would try the simple first.

Django ValidationError - how to use this properly?

Currently there is code that is doing (from with the form):
# the exception that gets raised if the form is not valid
raise forms.ValidationError("there was an error");
# here is where form.is_valid is called
form.is_valid() == False:
response['msg']=str(form.errors)
response['err']='row not updated.'
json = simplejson.dumps( response ) #this json will get returned from the view.
The problem with this, is that it is sending err message to the client as:
__all__"There was an error."
I want to remove the "all" garbage from the error template that is returned. How can I go about doing this? it seems to get added deep in django form code.
It's because the error is not associated with any field in particular, but it's so called non-field error.
If you're only interested in non-field errors, just simply pass this to the response:
response['msg']=str(form.errors['__all__'])
errors is an instance of a subclass of dict with some special rendering code. Most of the keys are the fields of the form, but as the docs describe, raising ValidationError in clean produces an error message that isn't associated with any particular field:
Note that any errors raised by your Form.clean() override will not be associated with any field in particular. They go into a special “field” (called __all__), which you can access via the non_field_errors() method if you need to. If you want to attach errors to a specific field in the form, you will need to access the _errors attribute on the form, which is described later.
https://docs.djangoproject.com/en/dev/ref/forms/validation/
So you can either generate your string representation of the errors differently (probably starting with form.errors.values() or form.errors.itervalues(), and maybe using the as_text method of the default ErrorList class) or associate your error with a particular field of the form as described in the docs:
When you really do need to attach the error to a particular field, you should store (or amend) a key in the Form._errors attribute. This attribute is an instance of a django.forms.utils.ErrorDict class. Essentially, though, it’s just a dictionary. There is a key in the dictionary for each field in the form that has an error. Each value in the dictionary is a django.forms.utils.ErrorList instance, which is a list that knows how to display itself in different ways. So you can treat _errors as a dictionary mapping field names to lists.
If you want to add a new error to a particular field, you should check whether the key already exists in self._errors or not. If not, create a new entry for the given key, holding an empty ErrorList instance. In either case, you can then append your error message to the list for the field name in question and it will be displayed when the form is displayed.
https://docs.djangoproject.com/en/dev/ref/forms/validation/#form-subclasses-and-modifying-field-errors

Implementing unread/read checking for a message

I'm having a message model. To this model I want to add a read/unread field, which I did by using a boolean field. Now, if someone reads this message, I want this boolean field to be turned to true. I access these messages at different parts in my app, so updating the field manually is going to be tedious.
Is there any way I can get some messages according to some condition, and when the message is fetched from db, the field gets auto updated?
Why don't you create a read_message() method on a custom model manager. Have this method return the messages you want, whilst also updating the field on each message returned.
You new method allow you to replace Message.objects.get() with Message.objects.read_message()
class MessageManager(models.Manager):
def read_message(self, message_id):
# This won't fail quietly it'll raise an ObjectDoesNotExist exception
message = super(MessageManager, self).get(pk=message_id)
message.read = True
message.save()
return message
Then include the manager on your model -
class Message(models.Model):
objects = MessageManager()
Obviously you could write other methods that return querysets whilst marking all the messages returned as read.
If you don't want to update your code (places where you call Message.objects.get()), then you could always actually override get() so that it updates the read field. Just replace the read_message function name above with get.
Depending on your database management system, you may be able to install a trigger:
PostgreSQL: http://www.postgresql.org/docs/9.1/static/sql-createtrigger.html
MySQL: http://dev.mysql.com/doc/refman/5.0/en/triggers.html
SQLite: http://www.sqlite.org/lang_createtrigger.html
Of course, this will need to be done manually in the database - outside of the Django application.