Prepopulate Django ModelForm With all records - django

I am trying to prepopulate my form with all records but i am receiving error
'QuerySet' object has no attribute '_meta' ,
I tried just passing my queryset through initial while i get no error the fields in my template aren't rendering initial={runninghours}
my views.py:
def runninghours(request):
runninghours = RunningHours.objects.all()
form = RunningHoursModelForm()
form_populated = RunningHoursModelForm(initial={runninghours})
if request.method == 'POST' and 'form-create' in request.POST:
form = RunningHoursModelForm(request.POST)
if form.is_valid():
form.save()
return redirect(request.path_info)
context = {"form": form,
"rhs": form_populated}
return render(request, 'runninghours.html', context)
The template:
<form method="POST" action="">
{% csrf_token %}
<td>{% render_field rhs.name %}</td>
<td>{% render_field rhs.current_runninghours %}</td>
<td>{% render_field rhs.last_runninghours %}</td>
<td>{% render_field rhs.last_modified %}</td>
<td> <input type="hidden" name="update_runninghours_id" value="{{ rhs.pk }}" />
<input name="form-delete" type="submit" class="button1" value='Update Runninghours' /></td>
</form>
the models.py:
class RunningHours(models.Model):
name = models.CharField(max_length=100)
current_runninghours = models.IntegerField(blank=True,null=True)
last_runninghours = models.IntegerField(blank=True,null=True,default=0)
last_modified = models.DateField(("Date"), default=datetime.date.today)
forms.py:
class RunningHoursModelForm(forms.ModelForm):
model = RunningHours
fields = ("name",
"current_runninghours",
"last_runninghours",)

I don't think you can pass a queryset, runninghours by using initial. That takes a dictionary as argument. What you might be able to do is use the queryset in making the form.
# forms.py
class RunningHoursModelForm(ModelForm):
def __init__(self,company,*args,**kwargs):
super (RunningHoursModelForm,self ).__init__(*args,**kwargs)
self.fields['the_field_you_want'].queryset = RunningHours.objects.all()
class Meta:
model = RunningHours
If you want to populate the form with an instance of RunningHours, you can just do
particularRunningHours = RunningHours.objects.get(name=name)
form_populated = RunningHoursModelForm(initial={
'current_runninghours': particularRunningHours.current_runninghours,
...
})

Related

Django crispy forms - bootstrap4 table_inline_formset template rendering extra row on top

I am using the bootstrap4/table_inline_formset.html template in a FormHelper from django-crispy-forms. The table is rendered correctly in the template, but an extra form always appears at the beginning of the table, which is not visible when submitting the form.
forms.py:
class MetricForm(forms.ModelForm):
class Meta:
model = Metric
exclude = ['auto_value','occurrence']
class MetricFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(MetricFormSetHelper, self).__init__(*args, **kwargs)
self.add_input(Submit('submit', 'Submit', css_class="btn btn-success"))
self.template = 'bootstrap4/table_inline_formset.html'
views.py:
#login_required
def create_occurrence(request, pk):
try:
site = Site.objects.get(id=pk)
except Site.DoesNotExist:
raise Http404("Site does not exist")
form = OccurrenceForm(request.POST or None, initial={'site':site})
MetricFormset = modelformset_factory(Metric, form=MetricForm, extra=3)
formset = MetricFormset(queryset=Metric.objects.none())
helper = MetricFormSetHelper()
if form.is_valid():
occurrence = form.save(commit=False)
occurrence.added_by = request.user
occurrence.site = site
occurrence.save()
form.save_m2m()
metric_formset = MetricFormset(request.POST)
if metric_formset.is_valid():
for metric_form in metric_formset.forms:
if all([metric_form.is_valid(), metric_form.cleaned_data != {}]):
metric = metric_form.save(commit=False)
metric.occurrence = occurrence
metric.save()
messages.success(request, "Occurrence created successfully.")
execute_from_command_line(["../manage_dev.sh", "updatelayers", "-s", "archaeology"])
return redirect(occurrence.get_absolute_url())
context = {
'form': form,
'site':site,
'formset':formset,
'helper': helper,
}
return render(request, "archaeology/occurrence_form.html", context=context)
template:
...
<form action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<h4>Metrics</h4>
{{ formset.management_form }}
{% crispy formset helper %}
{% if form.instance.pk != None %}
<a class="btn btn-danger" href="{% url 'delete_occurrence' occurrence.id %}">{% trans "Delete" %}</a>
{% endif %}
</form>
...
Any idea how to remove the extra row?
I had to change the template and remove the lines that printed an empty form at the beginning.
table_inline_formset.html:
<tr class="d-none empty-form">
{% for field in formset.empty_form %}
{% include 'bootstrap4/field.html' with tag="td" form_show_labels=False %}
{% endfor %}
</tr>

Django booleanfield modelform

So I'm making a to-do list and I made a booleanfield modelform which has attribute "complete". I want that user to check it when it's complete and I tried wriring if task.complete == True cross out the item and it didn't work(it only worked when I checked it from the admin panel). Then I tried form.complete instead of task.complete and it doesn't do anything.
models:
class Task(models.Model):
title = models.CharField(max_length=200)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
forms:
from .models import *
class TaskForm(forms.ModelForm):
title = forms.CharField(widget= forms.TextInput(attrs={'placeholder':'Add new task...'}))
class Meta:
model = Task
fields = '__all__'
html:
<div class="main-container">
<form method="POST" action="/">
{% csrf_token %}
<input type="submit"/>
{{ form.title }}
</form>
{% for task in tasks %}
{{ form.complete }}
{% if form.complete == True %}
<h1><strike>{{task.title}}</strike></h1>
{% else %}
<h1>{{task.title}}</h1>
{% endif %}
{% endfor %}
</div>
views:
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
context = {
'tasks': tasks,
'form': form,
}
return render(request, 'to-do.html', context)
There are some problems with your code I don't know how to explain. Try this. It should work.
<div class="main-container">
<form method="POST" action="/"> # create new task
{% csrf_token %}
{{ form.title }}
<input type="submit" name="new-task"/>
</form>
<form method="post"> # update task status
{% csrf_token %}
{% for task in tasks %}
{% if task.complete %}
<input type="checkbox" name="if_completed" checked value="{{ task.pk }}">
<h1><strike>{{task.title}}</strike></h1>
{% else %}
<input type="checkbox" name="if_completed" value="{{ task.pk }}">
<h1>{{task.title}}</h1>
{% endif %}
{% endfor %}
<input type="submit" name="update-task"/>
</form>
</div>
view.py (Only for form, add your other code with it)
from django.http import HttpResponseRedirect
from django.urls import reverse
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
if request.method == 'POST':
if 'new-task' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('home')) # replace home with url name where you want to redirect
elif 'update-task' in request.POST:
task_pk = request.POST.getlist("if_completed")
for i in task_pk:
Task.objects.filter(pk=i).update(complete=True) # I have replace pk with i here
return HttpResponseRedirect(reverse('home')) # replace home with url name where you want to redirect
context = {
'tasks': tasks,
'form': form,
}
return render(request, 'to-do.html', context)
in forms.py
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ('title',)
widgets = {
'title': forms.TextInput(attrs={'placeholder':'Add new task...'})
}
This should work. There may be be some error because of typos or indentation. Let me know if you get any issue
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
context = {
'tasks': tasks,
'form': form,
}
if request.method == 'POST':
if 'new-task' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
form.save()
elif 'update-task' in request.POST:
task_pk = request.POST.getlist("if_completed")
for i in task_pk:
Task.objects.filter(pk=pk).update(complete=True)
return render(request, 'to-do.html', context)

Error in as_crispy_field got passed an invalid or inexistent field

The page returns the error as_crispy_field got passed an invalid or inexistent field after SUBMIT Button is clicked. I was trying to submit the form when the error is raised. The record is SUCCESSFULLY saved into the database but an error is raised. A form for the template was created to set up the fields. The Form was instantiated in the views so that it could easily map the elements from the FORM to the TEMPLATE.
What caused the error and how can I resolve it?
FORM: Here is my form code
class ModelCreateDescriptionForm(forms.ModelForm):
name = forms.CharField(max_length=100)
description = forms.Textarea()
model_type = forms.ChoiceField(
widget = forms.Select,
choices = MODEL_TYPE_CHOICES,
)
def __init__(self, project_id=1, *args, **kwargs):
super(ModelCreateDescriptionForm, self).__init__(*args, **kwargs)
# project = Project.objects.get(id=project_id)
self.fields['model_type'].choices = MODEL_TYPE_CHOICES
self.fields['model_type'].required = True
class Meta:
model = Model
fields = ['name', 'description', 'model_type']
VIEW: Here is my view
def modelCreationDescription(request, **kwargs):
pk = kwargs.get('pk')
project = Project.objects.get(id=pk)
context = {}
context['project'] = project
if request.method == 'POST':
form = ModelCreateDescriptionForm(pk, request.POST)
name = request.POST.get(u'name')
description = request.POST.get(u'description')
model_type = request.POST.get(u'model_type')
if not(name and model_type):
messages.warning(request, f'Please fill model name and model type!')
if form.is_valid():
formDescription = form.save(commit=False)
try:
formDescription.name = name
formDescription.description = description
formDescription.model_type = model_type
formDescription.project = project
except:
messages.warning(request, f'Something wrong!')
return redirect('all-model-listview')
# save the form value
formDescription.save()
messages.success(request, f'Model description successfully created')
return render(request, 'models/pred_steps/modelSetTargetFeatures.html', {'model_form': formDescription })
else:
form = ModelCreateDescriptionForm(project_id=pk)
context = {
'form': form,
'project': project,
'create_model_description': True,
}
return render(request, 'models/pred_steps/modelCreateDescriptions.html', context)
HTML: This is the template that returning an error
<div class="card border-left-info mb-1 shadow">
<div class="col-xl-10 col-md-8 mb-1">
{% if project %}
<p><h5 class="text-info">Project: {{ project }}</h5></p>
{% endif %}
<form method="POST">
{% csrf_token %}
<fieldset class='form-group'>
{{ form.name|as_crispy_field }}
</fieldset>
<fieldset class='form-group'>
{{ form.description|as_crispy_field }}
</fieldset>
<fieldset class='form-group'>
{{ form.model_type|as_crispy_field }}
</fieldset>
<div class="form-group">
{% if project.id %}
<a class="btn btn-outline-secondary float-right" href="{% url 'all-model-listview' %}">Cancel</a>
{% endif %}
<button class="btn btn-outline-success" type="submit">Submit and Continue</button>
</div>
</form>
</div>
</div>
In case the next template is causing the error here are the codes
FORM:
class ModelSetTargetFeaturesForm(forms.ModelForm):
target_column_classification = forms.ChoiceField(
widget = forms.Select,
)
target_column_regression = forms.ChoiceField(
widget = forms.Select,
)
def __init__(self, project_id=1, *args, **kwargs):
super(ModelSetTargetFeaturesForm, self).__init__(*args, **kwargs)
project = Project.objects.get(id=project_id)
df = pd.read_csv(project.base_file, encoding='ISO-8859-1')
cols = df.columns
a_cols = np.column_stack(([cols, cols]))
self.fields['target_column_classification'].choices = a_cols
self.fields['target_column_classification'].required=True
self.fields['target_column_regression'].choices = a_cols
self.fields['target_column_regression'].required=True
# project = Project.objects.get(id=project_id)
class Meta:
model = Model
fields = ['target_column_classification', 'target_column_regression', ]
VIEW:
def modelSetTargetFeatures(request, **kwargs):
pk = kwargs.get('pk')
model = Model.objects.get(id=pk)
project = Model.objects.get(project=model.project.pk)
context = {}
context['model'] = model
context['project'] = project
if request.method == 'POST':
form = ModelSetTargetFeaturesForm(pk,request.POST)
target_column_classification = request.POST.get(u'target_column_classification')
target_column_regression = request.POST.get(u'target_column_regression')
if not(target_column_regression and target_column_classification):
messages.warning(request, f'Please fill model name and model type!')
if form.is_valid():
formTargetFeatures = form.save(commit=False)
formTargetFeatures.target_column_classification = target_column_classification
formTargetFeatures.target_column_regression = target_column_regression
# save the form value
formTargetFeatures.save()
messages.success(request, f'Model description successfully created')
return render(request, 'models/pred_steps/modelFeaturesSelection.html', {'model_form': formTargetFeatures })
else:
form = ModelSetTargetFeaturesForm(model=pk)
context = {
'form': form,
'project': project,
'model': model,
'create_model_description': True,
}
return render(request, 'models/pred_steps/modelSetTargetFeatures.html', context)
TEMPLATE:
<div class="card border-left-info mb-1 shadow">
<div class="col-xl-10 col-md-8 mb-1">
{% if project %}
<p><h5 class="text-info">Project: {{ project }}</h5></p>
<p><h5 class="text-info">Model: {{ name }}</h5></p>
{% endif %}
<form method="POST">
{% csrf_token %}
<fieldset class='form-group'>
{{ form.target_column_classification|as_crispy_field }}
</fieldset>
<fieldset class='form-group'>
{{ form.target_column_regression|as_crispy_field }}
</fieldset>
<div class="form-group">
{% if project.id %}
<a class="btn btn-outline-secondary float-right" href="{% url 'all-model-listview' %}">Cancel</a>
{% endif %}
<button class="btn btn-outline-success" type="submit">Next: Feature Selection</button>
</div>
</form>
</div>
</div>
Your view is making use of two different html templates:
'models/pred_steps/modelCreateDescriptions.html'
and
'models/pred_steps/modelSetTargetFeatures.html'
The first one is used for presenting the form, upon a GET request, and allowing the participant to input their data: return render(request, 'models/pred_steps/modelCreateDescriptions.html', context)
However, once the participant's data are POST'ed, this happens:
form = ModelCreateDescriptionForm(pk, request.POST)
# ... inside of if_valid
formDescription = form.save(commit=False)
# ...
formDescription.save()
# ...
return render(request, 'models/pred_steps/modelSetTargetFeatures.html', {'form': formDescription })
It's this second rendition that is causing problems, so I assume you are using crispy fields in the modelSetTargetFeatures.html template also. When rendering this other template, you seem to try to include formDescription as a form. However, formDescription is not a form, because form.save(commit=False) returns a Model object, not a form. Hence, django/crispy forms doesn't understand what's going on and rightly says that what you are trying to use as a form is not a valid form (since it's actually a Model instance). If you really do want to pass the form itself on to the other template, you can simply use {'form': form}.
You might also want to use a better name for your model than simply Model. It's very confusing and might cause bugs, since the name is identical to that of django.db.models.Model, which we subclass from for creating specific Models. Additionally, you might want to use the pythonic naming convention of using snakecase (e. g. my_function_based_view) for functions, and camelcase with the first letter capitalized for classes (e. g. MyFormClass).
(note that the above response refers to the code as you originally posted it - you've done some edits since I started responding, but the basic issue seems to be the same even after the edits)

How do i make many to many field as a chekbox items in template.?

I have 3 models one is Category(Fields = category_name) and another one is SubSategory(Fields = category(ForeignKey to Category),sub_category).and another model is DummyModel.
# Model
class DummyModel(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField()
category = models.ManyToManyField(Category)
sub_category = models.ManyToManyField(SubCategory)
This is my form
class StartProjectForm(ModelForm):
class Meta:
model = StartProject
fields = (
'name',
'email',
'category',
'sub_category',
)
def __init__(self, *args, **kwargs):
super(StartProjectForm, self).__init__(*args, **kwargs)
self.fields["category"].widget = CheckboxSelectMultiple()
self.fields["category"].queryset = Category.objects.all()
self.fields["sub_category"].widget = CheckboxSelectMultiple()
self.fields["sub_category"].queryset = SubCategory.objects.all()
def save(self, commit=True):
clean = self.cleaned_data.get
name = clean('name')
email = clean('email')
category = clean('category')
sub_category = clean('sub_category')
obj = StartProject()
obj.name = name
obj.email = email
obj.category = category
obj.sub_category = sub_category
obj.save()
This is my view
#view
class StartProjectView(View):
template_name = 'start-project.html'
def get(self, request):
form = StartProjectForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = StartProjectForm(request.POST)
if form.is_valid():
form.save()
form = StartProjectForm()
return render(request, self.template_name, {'form':form})
return HttpResponse("<h2>Done</h2>")
This is my Template
# Template
<form method="post">
{% csrf_token %}
<p>name: <input type="text" name="name"></p>
<p>Email: <input type="text" name="email"></p>
{% for form in form %}
<input type="checkbox" name="category">{{ form.category }}
{% endfor %}
<br>
{% for form in form %}
<input type="checkbox" name="sub_category">{{ form.sub_category }}
{% endfor %}
<button type="submit">Start Now</button>
</form>
I want category and subcategory in my template as checkbox items. How do I do that.?
After digging in your needs, what you are looking for is {{ form.FIELD_NAMEĀ }}.
Whit your form {{ form.category }} and {{ form.sub_category }} should work.
Take into account that this only renders the input itself, nor labels or other DOM elements.
Review the docs on 'Rendering fields manually' for more info -> https://docs.djangoproject.com/en/2.2/topics/forms/#rendering-fields-manually

Edit Model data using ModelForm: ModelForm validation error

I am working on my first django app. I am building an app that allows the user to rate beer. I want my user to be able to edit an entry they've already created. I take them to a ModelForm, and ask for their entry. When the POST method is called, my data is invalid. Here is my model.py:
from django.db import models
class Rating(models.Model):
beer_name = models.TextField()
score = models.DecimalField(max_digits=2, decimal_places=1)
notes = models.TextField(blank=True)
brewer = models.TextField(blank=True)
and forms.py:
from django import forms
from ratings.models import Rating
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
fields = ['beer_name', 'score', 'notes', 'brewer']
Here is the views.py of my edit function:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
if request.method == "POST":
form = RatingForm(request.POST, instance=rating)
if form.is_valid():
form.save()
return redirect(home)
else:
return HttpResponse("Invalid entry.")
else:
context = {'form': rating}
form = RatingForm(instance=rating)
return render(
request,
'ratings/entry_def.html',
context
)
However, every time the POST is called I get an "Invalid entry." HttpResponse, meaning my form.is_valid() is being returned False. Here is my template:
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Edit Rating</h2>
<form role="form" method="post">
{% csrf_token %}
<p>Beer Name: <textarea>{{ form.beer_name }}</textarea></p>
<p>Score: <input type="text" name="BeerScore" value="{{ form.score }}"></p>
<p>Notes: <textarea>{{ form.notes }}</textarea></p>
<p>Brewer: <textarea>{{ form.brewer }}</textarea></p>
<p><button type="submit" class="save btn btn-primary">Save</button></p>
<p><button type="reset" class="btn btn-primary">Cancel</button></p>
</form>
</div>
</div>
</div>
{% endblock %}
So when I press my Save button, I am getting the response. Here is my edit url in urls.py:
urlpatterns = [
...
url(r'rating/edit/(?P<row_id>[0-9]+)/$', edit , name='rating-edit'),
]
You're wrapping fields in other fields which don't have name attributes. This is most likely causing the values to be excluded from the request.POST data.
Additionally, Django form fields all have a corresponding HTML widget. So there's really no need to render the HTML by hand, unless you need to.
Change your template code to:
<p>
{{ form.beer_name.label }}: {{ form.beer_name }}
{% if form.beer_name.errors %}
<br />{{ form.beer_name.errors }}
{% endif %}{# repeat for other fields as needed #}
</p>
<p>{{ form.score.label }}: {{ form.score }}</p>
<p>{{ form.notes.label }}: {{ form.notes }}</p>
<p>{{ form.brewer.label }}: {{ form.brewer }}</p>
<p><button type="submit" class="save btn btn-primary">Save</button></p>
<p><button type="reset" class="btn btn-primary">Cancel</button></p>
If you need to change the widget, do so at the form class level:
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
def __init__(self, *args, **kwargs):
super(RatingForm, self).__init__(*args, **kwargs)
self.fields['notes'].widget = forms.Textarea()
This way, Django manages the attributes and binding for you.
Your view can also use some cleanup:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
form = RatingForm(request.POST or None, instance=rating)
if request.method == "POST" and form.is_valid():
form.save()
return redirect(home)
context = {'form': rating}
return render(request, 'ratings/entry_def.html', context)