send arguments to django forms - django

I want to pre-populate a textfield on a django form when the form is loaded. Below is my code.
I want this "content" field in forms.py to be pre-populated when my form loads in the browser. This field should be un-editable. But with the code below an empty textfield gets created.
I know I can pre-populate this info by sending it in the url string just like I am sending 'id'. But I don't want to take that approach. Is there any other way to send parameters to forms?
forms.py
class ContentModelForm(ModelForm):
content = forms.CharField(max_length=256)
message = forms.CharField(max_length=256)
created_at = forms.DateTimeField('DateTime created')
class Meta:
model = Content
def __init__(self, *args, **kwargs):
super(ContentModelForm, self).__init__()
self.fields['content'].value = kwargs.get('content')
views.py
def post_form_upload(request, id):
post = get_object_or_404(models.Post, id=id)
content = post.content
if request.method == 'GET':
form = ContentModelForm(content = content)
else:
form = ContentModelForm(request.POST)
if form.is_valid():
message = form.cleaned_data['message']
created_at = form.cleaned_data['created_at']
models.Content.objects.create(post_id = id,
message = message,
created_at = created_at)
return HttpResponseRedirect(reverse('post_form_upload',
args = (post.next_id,)))
return render(request, 'survey_forms/post_form_upload.html',{
'form':form,
'id' : id,
})
survey_forms/post_form_upload.html
<form action="{% url 'post_form_upload' id=id %}" method='post'>
{% csrf_token %}
{{form.as_p}}
<input type='submit' value='Submit'/>
</form>
Thanks.

You should be using the initial keyword argument when instantiating your form. This accepts a dictionary where the keys are the field name and the values are the initial values.
form = ContentModelForm(initial={'content': content})
Moreover since you want this field to be readonly. You should change this line
content = forms.CharField(max_length=256)
to
content = forms.CharField(max_length=256, widget=forms.TextInput(attrs={'readonly':'readonly'})

if you want an input to be pre-populated then you might as well just give it a "value" attribute, for example.
input<type="text, value="content_you_want_in_there">

Related

Django ModelForm saves and fetches <QuerySet object> instead of values

I have a simple Django 3.1.0 app I need to create in order to assign Tasks with Tags (or assign tags into tasks).
Model
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
tags = models.CharField(max_length=100, default="None", null=True)
class Tag(models.Model):
tag = models.CharField(max_length=30, default="No Tag")
members = models.ManyToManyField('Task', related_name="tag")
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
view
def main(request):
model = Task.objects.values().all()
tags = Tag.objects.values().all()
form = TaskForm()
con = {'context': list(model), 'form': form, 'tags': list(tags)}
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = TaskForm()
return render(request, "tasks.html", con)
form
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ['user', 'task', 'tags']
template_name = 'tasks.html'
tags = ModelMultipleChoiceField(
queryset= Tag.objects.all(),
widget=CheckboxSelectMultiple(), required=False,
)
task_form
<form method="post" class="form">
{% csrf_token %}
{{form}}
<input type="submit" value="Save">
</form>
This returns in the tags list the items listed as:
Tag object (1)
Tag object (2)
And when it saves when i press submit, it fetches in a table (in another template), the values saved in the text of <QuerySet [<Tag: Tag object (2)>]>
That's how it stores them in the database.
I have managed to extract the values as they are ('jenkins','AKS') and send them in the template using this (bootstrapvuejs) : {% for tag in tags %}<b-form-checkbox>{{tag.tag}}</b-form-checkbox>{% endfor %}, which lists them raw values perfectly.
However, when I do that modification, the form submitted is not written to database.
What am I missing?
UPDATE!
I have partly solved it by adding this into the Tag model:
def __str__(self):
return self.tag
but when it persists it on submit, it still saves it as:
<QuerySet [<Tag: jenkins>]>
So, how and where do I strip only the specific tag values to be inserted in the database?
Many Thanks
Alright so there is a couple issues with your code, first off your main view:
Change it from this:
def main(request):
model = Task.objects.values().all() # calling values without specifying an argument makes no sense so just call it like **Task.objects.all()**
tags = Tag.objects.values().all() # same here
form = TaskForm() # don't call your form here it gets reassigned later anyways
con = {'context': list(model), 'form': form, 'tags': list(tags)} # don't define your context here since you are reasigning your form later so the form instance is always TaskForm()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = TaskForm()
return render(request, "tasks.html", con)
To this:
def main(request):
model = Task.objects.all()
tags = Tag.objects.all()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = TaskForm()
context = {'tasks': model,
'form': form,
'tags': tags}
return render(request, "tasks.html", con)
Then in your template pass your form with as_p method call:
{{ form.as_p }}
Hovewer the error you are getting is not because of your html or your view, it's because your tags field in your Task model is not a ManyToMany relationship to your Tag model but rather a simple CharacterField and you are trying to save objects to the CharField, so rewrite your Task model like this:
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
tags = models.ManyToMany(Tags)
Then your form should save them in the tags field of your Task instance and you can view them like this:
task = Task.objects.get(pk=1)
task_tags = task.tags.all() # stores a queryset of all tags of the queried task
and in the template:
{% for tag in task.tags.all %}
...
{% endfor %}
OK , I solved the POST data that is saved in database as Queryset, by extracting in the view where save() is called, the field 'tags' likewise:
f = form.save(commit=False)
f.tags = request.POST['tags']
form.save()
The only problem now is that I have multiple checkboxes in the form but this way it extracts only one of them, whilst I would expect it to return a list like what is printed in the request.POST : <QueryDict: {'csrfmiddlewaretoken': ['XV7HgTFiWXEnrkhqT3IsqUN2JbnT7YIH5r6fKgh2ehqeLsLMpvCPdUU4N2qwWuPk'], 'user': ['afa'], 'task': ['aff'], 'tags': ['jenkins', 'AKS']}> -> from that I call 'tags' but it saves only 'jenkins' ...
UPDATE
OK, I RTFM and saw that there is a method on the QueryDict object that can be passed to request.POST.getlist('tags') , so now it returns the complete value of 'tags' key.

How to upload file in django

this might be a pretty stupid question. Also I am new to django. But I was trying to create a basic file upload approach with django where user uploads a file and it gets stored into the defined media path (or whatever that it's called) and that the file size, name, and some other attributes that are needed can be stored into the database. So I have the model ready which will help you understand the question better.
class Document(models.Model):
file_uid = models.CharField(max_length = 16)
file_name = models.CharField(max_length = 255)
file_size = models.CharField(max_length = 255)
file_document = models.FileField(upload_to='uploaded_files/')
uploaded_on = models.DateTimeField(auto_now_add=True)
uploaded_by = models.CharField(max_length=16)
Now it's clearly plain that we don't need to create all the fields in the form and that most them can be received from the file itself (like the name, size). for other attrs like uid and uploaded by those also will be added by the backend. So that's where I am stuck. I have searched for 2 days straight and still couldn't find a proper solution.
As of now this is my views.py
def uploadView(request):
if(request.method == 'POST'):
form = FileUploadForm(request.POST, request.FILES)
uploaded_file = request.FILES['uploaded_file']
file_dict = {
'file_uid' : get_random_string(length=10),
'file_name' :uploaded_file.name,
'file_size' : uploaded_file.size,
'file_document' : request.FILES['uploaded_file'],
'uploaded_by' : get_random_string(length=10)
}
form = FileUploadForm(data=file_dict)
if form.is_valid():
form.save()
return HttpResponse("You reached here")
else:
return HttpResponse("Your form is invalid")
else:
form = FileUploadForm(request.POST, request.FILES)
return render(request, 'function/upload.html', {
'form':form
})
I don't know if this is correct but as of know the form.isvalid() is false.
here's my forms.py
class FileUploadForm(forms.ModelForm):
file_document = forms.FileField(widget=forms.FileInput(attrs={'name':'uploaded_file'}))
class Meta:
model = Document
fields = ('file_uid', 'file_name', 'file_size', 'file_document', 'uploaded_by')
and my upload page section looks like this
<body>
<h1>Upload a file</h1>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="uploaded_file">
<button type="submit">Upload</button>
</form>
</body>
If you can mercifully guide me into a proper way of doing this i'll be really gratefull.
Before solution, Here are few issues i found in your code
Major issue is how you tried to update the name of your file_document input, it doesn't work this way. confirm this by inspecting in devtools.
Checkout my answer here to update name attribute of django input form field.
Without updating this, you are not getting file from form.
Not issues just something i would like to point out
def uploadView(request):
if(request.method == 'POST'):
form = FileUploadForm(request.POST, request.FILES)
# your code in between, here the above form is never used and the overridden by the form in next line so why assigning it
form = FileUploadForm(data=file_dict)
# your form.is_valid() code start here
else:
form = FileUploadForm(request.POST, request.FILES)
# This block will only run for GET request, why using request.POST, request.FILES
return render(request, 'function/upload.html', {
'form':form
})
Here is how i got your code working
update FileUploadForm like this
class FileUploadForm(forms.ModelForm):
class Meta:
model = Document
fields = ('file_uid', 'file_name', 'file_size', 'file_document', 'uploaded_by')
# below code is only used to change the name of file_document to uploaded_file
custom_names = {'file_document': 'uploaded_file'}
def add_prefix(self, field_name):
field_name = self.custom_names.get(field_name, field_name)
return super(FileUploadForm, self).add_prefix(field_name)
use form in html like this
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{form.file_document}}
<input type="submit" value="send"/>
</form>
Update view as
def uploadView(request):
if(request.method == 'POST'):
uploaded_file = request.FILES['uploaded_file']
file_dict = {
'file_uid' : 'test1',
'file_name' :uploaded_file.name,
'file_size' : uploaded_file.size,
'uploaded_by' : 'hemant'
}
form = FileUploadForm(file_dict, request.FILES)
if form.is_valid():
form.save()
return HttpResponse("You reached here")
else:
return HttpResponse("Your form is invalid")
else:
form = FileUploadForm()
return render(request, 'function/upload.html', {
'form':form
})

ValueError: Cannot assign value: must be an instance

I have four fields in a model, one of which is a foreign key field and the other three are m2m fields. The form is opened in the modal, but the data is not being saved, Error given below. I don't understand what I did wrong. I would be very grateful for a little help.
Model:
class ProductAttributes(models.Model):
product = models.ForeignKey('Product', blank=True, null=True, on_delete=models.SET_NULL)
size = models.ManyToManyField('ProductSize', blank=True)
colour = models.ManyToManyField('ProductColour', blank=True)
cupsize = models.ManyToManyField('ProductCupSize', blank=True)
def __str__(self):
return self.product
Form:
class ProductAttributesForm(forms.ModelForm):
product = forms.IntegerField(label=('ID'),required=True, disabled=True)
size = forms.ModelMultipleChoiceField(queryset=ProductSize.objects.all(),widget=Select2MultipleWidget, required=False)
colour = forms.ModelMultipleChoiceField(queryset=ProductColour.objects.all(),widget=Select2MultipleWidget, required=False)
cupsize = forms.ModelMultipleChoiceField(queryset=ProductCupSize.objects.all(),widget=Select2MultipleWidget, required=False)
class Meta:
model = ProductAttributes
fields = ['product','size','colour','cupsize']
Template:
{% load crispy_forms_tags %}
<form id="Form" method="post" action="{% url 'accpack:products_attributes_create' product %}" class="js-product-create-form col s12" >
{% csrf_token %}
{% crispy form form.helper %}
</form>
View:
def save_attribute_form(request, form, template_name, pk):
data = dict()
if request.method == 'POST':
if form.is_valid():
form.save()
data['form_is_valid'] = True
else:
data['form_is_valid'] = False
context = {'form': form, 'product':pk}
data['html_form'] = render_to_string(template_name, context, request=request)
return JsonResponse(data)
def attribute_create(request, pk):
if request.method == 'POST':
form = ProductAttributesForm(request.POST, initial={'product': pk})
else:
form = ProductAttributesForm(initial={'product': pk})
return save_attribute_form(request, form, 'main/products/partial_product_attribute_form.html', pk)
ajax:
var saveForm = function () {
var form = $(this);
$.ajax({
url: form.attr("action"),
data: form.serialize(),
type: form.attr("method"),
dataType: 'json',
success: function (data) {
if (data.form_is_valid) {
$("#modal-product_attribute").modal("hide");
console.log(data.form_data);
}
else {
$("#modal-product_attribute .modal-content").html(data.html_form);
}
}
});
return false;
$("#modal-product_attribute").on("submit", ".js-product-create-form", saveForm);
error:
File "C:\ProgramData\Anaconda3\envs\djangoproject\lib\site-packages\django\db\models\fields\related_descriptors.py", line 220, in __set__
self.field.remote_field.model._meta.object_name,
ValueError: Cannot assign "111": "ProductAttributes.product" must be a "Product" instance.
You have an error in your forms.py.
class Meta:
model = ProductSize
fields = ['product','size','colour','cupsize']
Here, the model should be
model = ProductAttributes
Since the model you specified here ProductSize does exist, submitting form will just create another instance to ProductSize model without those specific fields and error messages. You can check your admin page.
EDIT ******
2. Based on what you added in the question and the error message, it looks like in attribute_create you are passing product's pk. Now in the attribute_create, you are passing initial dict {'product': pk}. This is wrong, because, in your ProductAttributesForm, the product field should be an instance rather than a pk.
You need to refer product as
product = Product.objects.filter(pk=pk)
initial = {'product':product}
The current error message will be gone, but you will have other errors.

ModelForm in Django not saving anything

I am making a twitter clone app in Django. I have a model, and a modelform as so:
Class Tweet(models.Model):
content = models.TextField(blank=True, null=True)
image = models.FileField(upload_to='images/', blank=True, null=True)
class TweetForm(forms.ModelForm):
class Meta:
model = Tweet
fields = ['content',]
def clean_content(self):
content = self.cleaned_data.get('content')
if len(content) > MAX_TWEET_LENGTH:
raise forms.ValidationError('This tweet is too long')
I have a view for this:
def tweet_create_view(request, *args, **kwargs):
if request.method == 'POST':
form = TweetForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
form = TweetForm()
context = {
'form': form
}
return render(request, 'components/form.html', context)
and the template:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-secondary" type="submit">Save</button>
</form>
When I submit the form data, in the database the value is shown as NULL, even if I pass in some text. What am I doing wrong??
You are not returning the cleaned form data from the clean function, as your clean function doesn't return anything if it passes the validation, it saves None/Null to DB.
def clean_content(self):
content = self.cleaned_data.get('content')
if len(content) > MAX_TWEET_LENGTH:
raise forms.ValidationError('This tweet is too long')
return content

Django 'QuerySet' object has no attribute 'split' using modelform

I'm having a problem getting my view to update a manytomany field. It returns this after the form is submitted.
Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/home/footbook/Ubuntu One/webapps/fb/poc/../poc/activity/views.py" in activity_save_page
44. group_names = form.cleaned_data['groups'].split()
Exception Type: AttributeError at /activity_save/
Exception Value: 'QuerySet' object has no attribute 'split'
Here are the files.
Models.py
class Group (models.Model):
group_nm = models.CharField(max_length=64)
group_desc = models.CharField(max_length=250)
created = models.DateTimeField(auto_now_add=True)
active_yn = models.BooleanField(default=True)
def __unicode__(self):
return self.group_nm
class Activity(models.Model):
activity_nm = models.CharField(max_length=60)
activity_desc = models.CharField(max_length=250)
startdt = models.DateField()
enddt = models.DateField()
crdt = models.DateTimeField(auto_now_add=True,editable=False)
groups = models.ManyToManyField(Group)
upddt = models.DateTimeField(editable=False)
def save(self, *args, **kwargs):
if not self.id:
self.crdt = datetime.date.today()
self.upddt = datetime.datetime.today()
super(Activity, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
forms.py
def make_custom_datefield(f):
formfield = f.formfield()
if isinstance(f, models.DateField):
formfield.widget.format = '%m/%d/%Y'
formfield.widget.attrs.update({'class':'datePicker', 'readonly':'true'})
return formfield
class ActivitySaveForm(forms.ModelForm):
formfield_callback = make_custom_datefield
def __init__(self, *args, **kwargs):
super(ActivitySaveForm, self).__init__(*args, **kwargs)
self.fields['activity_nm'].label = "Activity Name"
self.fields['activity_desc'].label = "Describe It"
self.fields['startdt'].label = "Start Date"
self.fields['enddt'].label = "End Date"
self.fields['groups'].label ="Group"
class Meta:
model = Activity
views.py
def activity_save_page(request):
if request.method == 'POST':
form = ActivitySaveForm(request.POST)
if form.is_valid():
act, created = Activity.objects.get_or_create(
activity_nm = form.cleaned_data['activity_nm']
)
act.activity_desc = form.cleaned_data['activity_desc']
if not created:
act.group_set.clear()
group_names = form.cleaned_data['groups'].split()
for group_name in group_names:
group, dummy = Group.objects.get_or_create(group_nm=group_name)
act.group_set.add(group)
act.save()
return HttpResponseRedirect('/activity/')
else:
form = ActivitySaveForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('activity_save.html', variables)
I think it would work if I wasn't using the modelform, but I need it to implement this datepicker. Since it's a manytomany field, I want to split them when they are entered into the database, but my queryset fails. I've tried changing this a bunch of different ways, but I'm stuck. I've seen a lot of similar questions, but they either had foreign keys or no modelform.
Thanks.
EDIT:
activity_save.html
{% extends "base.html" %}
{% block title %}Save Activity{% endblock %}
{% block head %}Save Activty{% endblock %}
<input class="datePicker" readonly="true" type="text" id="id_startdt" />
<input class="datePicker" readonly="true" type="text" id="id_enddt" />
{% block content %}
<form action="{% url activity.views.activity_save_page act_id%}" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="save it" />
</form>
{% endblock %}
Exactly as the error describes: a QuerySet does not have a split method. You cannot call my_qs.split().
form.cleaned_data['groups'] returns cleaned data; it has already taken care of the form string-to-python-object conversion for you, which in the case of a ManyToManyField is ultimately represented by a QuerySet in python.
A date field returns date objects, IntegerField an integer, CharFields a string, etc. in the same way via form cleaning.
If you want a list of group_names, you'd need to explicitly iterate through the objects in the QuerySet and pull their group_nm attribute.
group_names = [x.group_nm for x in form.cleaned_data['groups']]
I'm not sure you need to do all that in your view. You can directly save the form in the view without manually creating the objects and manipulating them.
Also, you need to get the id of activity so that you can update existing activity instance.
Update the urls.py to have these urls to have act_id:
url(r'^activity_app/save/(?P<act_id>\d+)/$', 'activity_app.views.activity_save_page'),
url(r'^activity_app/save/$', 'activity_app.views.activity_save_page'),
I would change the view to:
def activity_save_page(request, act_id=None):
act_inst = None
try:
if act_id:
act_inst = Activity.objects.get(id=act_id)
except Exception:
pass
if request.method == 'POST':
form = ActivitySaveForm(request.POST, instance=act_inst)
if form.is_valid():
return HttpResponseRedirect('/activity/')
else:
form = ActivitySaveForm(instance=act_inst)
variables = RequestContext(request, {
'form': form
})
return render_to_response('activity_save.html', variables)