How do I save a Django Model from request.POST? - django

I'm trying to save a new object from a django model using a POST data querydict. This is part of a PISTON handler. I've seen this done in numerous examples, but I just can't get it to work.
Here is my code:
class FestHandler(BaseHandler):
model = Deal
def create(self, request):
"""
Creates a new fest.
"""
attrs = self.flatten_dict(request.POST)
postcopy = request.POST.copy()
if self.exists(**attrs):
return rc.DUPLICATE_ENTRY
else:
loc = Location.objects.get(pk=attrs['location'])
postcopy['location'] = loc
fest = Fest(postcopy)
fest.save()
return fest
Here is the error I receive every time:
Exception was: int() argument must be a string or a number, not 'QueryDict'
I realize what the error means, so basically I am asking how I can save a new "Fest" by passing in the whole POST dictionary without having to type in the keys manually every time like this:
loc = Location.objects.get(pk=attrs['location'])
fest = Fest(
location=loc,
name=attrs['name'],
description=attrs['description'],
details=attrs['details'],
)
Thanks for your help!

First, I think you'll be happier of you explicitly make your PK's into integers.
loc = Location.objects.get(pk=int(attrs['location']))
Second, you should use a Form.
It validates the fields for you.
It will create the Model object from the Form object.
Read this. http://docs.djangoproject.com/en/1.2/topics/forms/modelforms/

Related

(Flask) WTForm gets rendered with wrong values

I am currently working on a web-application based "database-explorer" for a university project.
Basically I create a site for each relation of the database, where the user can view the data and additionally I want them to be able to add data.
For that I use wtforms. I take the inputs, create a simple "SQL string" with it and execute it.
To make the input easier, I want to use dynamic SelectFields.
This is my approute:
#app.route('/table/fact_angestellte')
def fact_angestellte():
if current_user.is_authenticated:
mycursor.execute("SELECT * FROM dim_rolle WHERE ro_rolle !='Ansprechpartner' AND ro_rolle != 'Teilnehmer';")
choicesRolle = mycursor.fetchall()
form = InsertAngestelltenForm(choicesRolle)
print(form.choicesRolle)
mycursor.execute("SELECT * FROM fact_angestellte INNER JOIN dim_rolle ON fact_angestellte.an_rolle_fk = dim_rolle.ro_id_pk;")
data = mycursor.fetchall()
return render_template('tables/fact_angestellte.html', data=data, form=form)
else:
return redirect(url_for('login'))
The form gets created successfully and if I try to print form.choicesRolle (in the approute), it also gives me the correct output in the console. But when I go on my website, the SelectField still has the default value of choicesRolle.
class InsertAngestelltenForm(FlaskForm):
choicesRolle =[]
nachname = StringField('Nachname', validators=[DataRequired()])
vorname = StringField('Vorname',validators=[DataRequired()])
geschlecht = SelectField('Geschlecht', choices=[('maennlich', 'männlich'), ('weiblich', 'weiblich')], validators=[DataRequired()])
postleitzahl = StringField('Postleitzahl | FK', validators=[DataRequired()])
strasse = StringField('Straße und Nummer', validators=[DataRequired()])
rolle = SelectField('Rolle', choices=choicesRolle, validators=[DataRequired()])
submit = SubmitField('Eintrag hinzufügen')
def __init__(self, choicesRolle):
super().__init__()
self.choicesRolle = choicesRolle
print(self.choicesRolle)
So my problem is: the object has the correct attributes, but somehow they don't "reach" the template.
Any help is appreciated.
Greetings
Per the WTForms documentation, the choices keyword is only evaluated once. In your example, this means it's evaluating to the empty array you set in the choicesRolle class attribute, and that's what's being passed to Flask. You need to set the choices after the form is instantiated, not during it.
On your form class, remove the entire __init__ method, the choicesRolle class attribute, and the choices parameter from the rolle SelectField. Then, in your fact_angestellte view function, set the form's choices after you instantiate it, as follows:
choicesRolle = mycursor.fetchall()
form = InsertAngestelltenForm()
form.rolle.choices = choicesRolle
This should work...let me know. Note that I'm not sure what data is being returned from mycursor.fetchall() as you don't really describe, but the SelectField choices needs to be a list of values...WTForms by default coerces each value to unicode.

ModelForm.is_valid() is False when row already exists. How to get the object?

My Location model has a unique filed called name. Through LocationForm I get user input and try to either insert it into the database or get the axisting object. But if user inputs an existing name, lf.is_valid() becomes False and I never get to the get_or_create command. Any idea how I can pass this?
lf = LocationForm(request.POST or None)
if lf.is_valid():
location_instance, created = Location.objects.get_or_create(**lf.cleaned_data)
Thanks,
If you're using ModelForm then maybe you can try overriding _get_validation_exclusions method.
https://github.com/django/django/blob/8838d4dd498c8f66ea4237fe8a79a5f77d6f95c9/django/forms/models.py#L297
class LocationForm():
def _get_validation_exclusions(self):
exclude = super(LocationForm, self)._get_validation_exclusions()
exclude.append(self.fields['name'])
return exclude
Let me know how that goes..
Thanks for your thought.
I found a useful description of .is_valid in Django documentation. Django Validating Objects.
Apparently .is_valid also check against Model.validate_unique().
Here is what I ended up doing:
if lf.is_valid():
location_instance = lf.save()
return render (...)
else:
location_instance, created = Location.objects.get_or_create(**lf.cleaned_data)
error_msg = lf.errors["__all__"]
return render(...)

Django : Validate data by querying the database in a model form (using custom clean method)

I am trying to create a custom cleaning method which look in the db if the value of one specific data exists already and if yes raises an error.
I'm using a model form of a class (subsystem) who is inheriting from an other class (project).
I want to check if the sybsystem already exists or not when i try to add a new one in a form.
I get project name in my view function.
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def clean(self,project_name):
cleaned_data = super(SubsytemForm, self).clean(self,project_name)
form_subsystem_name = cleaned_data.get("subsystem_name")
Subsystem.objects.filter(project__project_name=project_name)
subsystem_objects=Subsystem.objects.filter(project__project_name=project_name)
nb_subsystem = subsystem_objects.count()
for i in range (nb_subsystem):
if (subsystem_objects[i].subsystem_name==form_subsystem_name):
msg = u"Subsystem already existing"
self._errors["subsystem_name"] = self.error_class([msg])
# These fields are no longer valid. Remove them from the
# cleaned data.
del cleaned_data["subsystem_name"]
return cleaned_data
My view function :
def addform(request,project_name):
if form.is_valid():
form=form.save(commit=False)
form.project_id=Project.objects.get(project_name=project_name).id
form.clean(form,project_name)
form.save()
This is not working and i don't know how to do.
I have the error : clean() takes exactly 2 arguments (1 given)
My model :
class Project(models.Model):
project_name = models.CharField("Project name", max_length=20)
Class Subsystem(models.Model):
subsystem_name = models.Charfield("Subsystem name", max_length=20)
projects = models.ForeignKey(Project)
There are quite a few things wrong with this code.
Firstly, you're not supposed to call clean explicitly. Django does it for you automatically when you call form.is_valid(). And because it's done automatically, you can't pass extra arguments. You need to pass the argument in when you instantiate the form, and keep it as an instance variable which your clean code can reference.
Secondly, the code is actually only validating a single field. So it should be done in a specific clean_fieldname method - ie clean_subsystem_name. That avoids the need for mucking about with _errors and deleting the unwanted data at the end.
Thirdly, if you ever find yourself getting a count of something, iterating through a range, then using that index to point back into the original list, you're doing it wrong. In Python, you should always iterate through the actual thing - in this case, the queryset - that you're interested in. However, in this case that is irrelevant anyway as you should query for the actual name directly in the database and check if it exists, rather than iterating through checking for matches.
So, putting it all together:
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def __init__(self, *args, **kwargs):
self.project_name = kwargs.pop('project_name', None)
super(SubsystemForm, self).__init__(*args, **kwargs)
def clean_subsystem_name(self):
form_subsystem_name = self.cleaned_data.get("subsystem_name")
existing = Subsystem.objects.filter(
project__project_name=self.project_name,
subsytem_name=form_subsystem_name
).exists()
if existing:
raise forms.ValidationError(u"Subsystem already existing")
return form_subsystem_name
When you do form=form.save(commit=False) you store a Subsystem instance in the variable form but the clean method is defined in SubsystemForm. Isn't it?

django request.POST field name substitution

I am using ajax to send data into a django view with data coming in via request.POST. I am posting the model field that needs to be updated as well as the model value. I just need to know how to use the field name variable I extract from request.POST['field_name'] so I can set the field in the model. Here is my code.
field_name = request.POST["field_name"]
field_value = request.POST["field_value"]
member_id = get_member_session(request).id
try:
member = Members.objects.get(id=member_id)
except:
status="ERROR-USER-DOES-NOT-EXIST"
return json_status(status)
try:
member.field_name=field_value
member.save()
return json_status('OK')
except:
status = "USER_SAVE_ERROR"
return json_status(status)
member.field_name is obviously not right. Do I need to use eval(field_name) or something like that? I would prefer not to if possible.
Many thanks
Rich
Use setattr, which allows you to set a variable attribute on an object:
try:
member._meta.get_field(field_name)
except member.FieldDoesNotExist:
# return something to indicate the field doesn't exist
return json_status('USER_FIELD_ERROR')
setattr(member, field_name, field_value)
member.save()
return json_status('OK')
edit: I updated to use model._meta.get_field, as it's a better approach. Mentioned in this answer for another question.

TypeError when saving a formset

I've declared a formset like so:
class BaseFeatureFormSet(BaseFormSet):
def save(self, commit = True):
feature = Feature(name = self.cleaned_data['name'],
type = self.cleaned_data['type'],
premium = self.cleaned_data['premium'],)
feature.save()
return feature
FeaturesFormset = formset_factory(EditFeatureForm,
formset = BaseFeatureFormSet, extra = 0)
So when I'm saving the formset, I'm getting a TypeError: list indices must be integers, not str referring to the first line of the save() function. How do I solve this error?
Update 1
Managed to solve this first bit thanks to gruszyczy. I'm not getting another TypeError: 'EditFeatureFormFormSet' object is not iterable from the next line in the code section:
for feature in features:
feature.save()
feature = vehicle.features.add(feature)
The error is from for feature in features: Ideas?
cleaned_data in this example is a list of form values. You have to iterate over it and inside you will find data you need:
for values in self.cleaned_data:
feature = Feature(name=values['name'], ..
That's because formset is a list of forms, that are displayed and returns a list of form values. This is a simple concept to grasp, when you simple realise, that FormSet <-> [Form, Form, Form, ..]
Where exactly is features defined as a list/tuple?