Django pass variables to form_class - python

I am trying to figure out how to access fields from a Model that is used as a ForeignKey within the Model that the forms are querying.
Each Room has a form where the user selects a Layout from a dynamic list of possible Layout objects.
1—The HTML forms/room/update/layouts.html
<form class="layouts__form form-horizontal" action="" method="post" enctype="multipart/form-data">
<fieldset class="form__options">
{% csrf_token %}
{% for field in form.layout %}
<div class="layouts__layout">
{{ field.tag }}
{{ field.choice_label }}
<label for="value_{{ forloop.counter0 }}">
<div class="layouts__layout__thumbnail layouts__layout__thumbnail--{{ field.choice_label }}" style="background-image: url('### (I WOULD LIKE TO LOAD 'Layout.thumbnail' HERE) ###');"></div>
</label>
</div>
{% endfor %}
<div class="form__submit">
<button type="submit">Submit</button>
</div>
</fieldset>
</form>
2—The form is being called by this in views.py:
class RoomLayoutView(UpdateView):
model = Room
form_class = RoomLayoutForm
template_name = 'forms/room/update/layouts.html'
3—Which is being created by this in forms.py:
class RoomLayoutForm(forms.ModelForm):
layout = forms.ModelChoiceField(
widget=forms.RadioSelect(attrs={'type': 'radio', 'id': 'value',}),
queryset=Layout.objects.all(),
required=False, empty_label=None)
class Meta:
model = Room
fields = ['layout']
4—Which uses the Room model from:
class Room(models.Model):
title = models.CharField(max_length=200, blank=True)
layout = models.ForeignKey(Layout, related_name='template_selected', blank=True, null=True)
def __str__(self):
return self.title
5—Which takes one of the Layout models as a ForeignKey defined here:
class Layout(models.Model):
title = models.CharField(max_length=200)
...
padding_top = models.IntegerField(blank=False, default=0)
...
thumbnail = models.FileField(upload_to='layouts')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-title',)
def __str__(self):
return self.title
I am trying to figure out how to access attributes from Layout model within the actual form. I would especially like to dynamically load the Layout.thumbnail or Layout.padding_top within the form at the top. I have tried at least 8 different methods and was unable to figure out a way to make this work. Any help would be really appreciated.

As stated in this answer, you can access the current instance associated with the form like this in your template:
{{ form.instance }}
So to access the thumbnail or padding_top attributes of the linked layout:
{{ form.instance.layout.thumbnail }}
{{ form.instance.layout.padding_top }}

Related

Check Selected Fields in Django

I have a form with musicial instruments:
class InstrumentForm(forms.ModelForm):
instruments = forms.ModelMultipleChoiceField(queryset=Instrument.objects.all())
class Meta:
model = Instrument
fields = ('instruments', )
That takes all instruments from model. I need to somehow check selected instruments and save them to Profile Model:
class Profile(models.Model):
...
instrument = models.ManyToManyField(Instrument, related_name='instruments')
def __str__(self):
return f'{self.user.username} Profile'
I also have a dummy html page with form, it works:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-outline-dark" type="submit">OK</button>
</form>
But I need to check all instruments that user has selected and save them to the user profile model, how can I do this?

I Use for loop inside django template, but Django Generic editing views don't send data to template

I'm very new to django. I'm creating student attendance system. I can add classes and students in each class. But when it comes to check attendance I get stuck. My model, form, view and template for checking attendance are here:
models.py
choice_list = [(1, 'Present'), (2, "Absent")]
class AttendanceChecking(models.Model):
pupil = models.ForeignKey(ClassPupils, on_delete=models.CASCADE, related_name='pupil_attendance')
pupil_class = models.ForeignKey(SchoolClassess, on_delete=models.CASCADE, related_name='class_attendance')
teacher = models.ForeignKey(User, on_delete=models.CASCADE, related_name='responsible_attendance')
date = models.DateField()
mark_attendance = models.CharField(max_length=100, choices=choice_list, default='Present')
forms.py
choice_list = [(1, 'Present'), (2, "Absent")]
class AttendanceForm(forms.ModelForm):
mark_attendance = forms.ChoiceField(choices=choice_list)
class Meta:
model = AttendanceChecking
fields = ('mark_attendance',)
views.py
class AttendanceListView(LoginRequiredMixin, FormView):
form_class = AttendanceForm
context_object_name = 'classes'
model = SchoolClassess
template_name = 'app_school/attendance_list_view.html'
attendance_list_view.html
{% extends 'base.html' %}
{% block content %}
<form method="POST">
{% if user.is_authenticated %}
{% for pupil in classes.pupils.all %}
{{ pupil.pupil_lname }} {{pupil.pupil_fname}}
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" name="">
{% endfor %}
{% endif %}
</form>
{% endblock %}
My purpose is creating a table which is the list of all students in a class and additional column for attendance as shown in image students list for checking attendance. when confirm button is clicked the data must be saved db table.
I tried, but when I use CreateView or FormView the data isn't showed in template with for loop.
If you can help me using generic editing views, I would be appreciated.

Setting initial field value in generic form through URL parameter

I'm struggling to set an initial value in a form instance based on the URL parameter in Django 3.0.
I have a Claim model:
# models.py
class Claim(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(default=timezone.now)
member = models.ForeignKey(User, on_delete=models.CASCADE)
I have a NewClaimForm based on ModelForm:
# forms.py
class NewClaimForm(forms.ModelForm):
class Meta:
model = Claim
fields = ['product', 'text']
I have a NewClaimView based on CreateView:
# views.py
class NewClaimView(LoginRequiredMixin, CreateView):
model = Claim
form_class = NewClaimForm
template_name = 'portal/new_claim.html'
def form_valid(self, form):
form.instance.member = self.request.user
return super(NewClaimView, self).form_valid(form)
And using the following template fragment on the index page...
# index.html
<div class="card-deck">
{% for product in products %}
<div class="card text-center">
<div class="card-header">
<h5 class="card-title text-primary">{{ product }}</h5>
</div>
<div class="card-body">
<ol class="card-text text-left">
<li>Fill in the {{ product }} form</li>
<li>Attach your medical records</li>
<li>Get your claim reviewed within 48 hours</li>
</ol>
Online Form
</div>
</div>
{% endfor %}
</div>
...I pass the product_id parameter to the URL:
# urls.py
app_name = 'portal'
urlpatterns = [
path('new_claim/<int:product_id>/', NewClaimView.as_view(), name='new_claim_product'),
]
And lastly, this is what my new_claim template looks like:
# new_claim.html
{% extends "portal/base.html" %}
{% load bootstrap4 %}
{% block content %}
<p>Submit a new claim</p>
<form action="{% url 'portal:new_claim' %}" method='post' class="form" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button name="submit">Submit claim</button>
{% endbuttons %}
</form>
{% endblock content %}
I would like to now use the product_id to set the initial value of the product field in the form instance according to product_id. How can I achieve this?
Figured out how to do this by modifying the get method for my class-based view:
# views.py
class NewClaimView(LoginRequiredMixin, CreateView):
model = Claim
form_class = NewClaimForm
template_name = 'portal/new_claim.html'
def get(self, request, product_id=None, *args, **kwargs):
form = self.form_class(initial={'product': product_id})
return render(request, self.template_name, {'form': form})
def form_valid(self, form):
form.instance.member = self.request.user
return super(NewClaimView, self).form_valid(form)
Here's a link to the relevant documentation.
Is this an optimal way to solve this?
Do I need the *args and **kwargs in the modified get method? I'm not using them in my code but perhaps it would be useful to keep them there for other purposes in the future?

Django adding to database with foreign key while still showing the informations from the other model

I want to add elements to my database (for model Student) while having stuff from another model (School) be displayed alongside the form for the Student.\
This is the models.py
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def get_absolute_url(self):
return reverse('basiccbv:detail', kwargs={'pk':self.pk})
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField(validators= [validators.MinValueValidator(1),validators.MaxValueValidator(20)],default=1)
school = models.ForeignKey(School, related_name='students')
def __str__(self):
return self.name
In my views.py I have this:
class SchoolDetailedView(DetailView):
context_object_name = 'school_detail'
model = models.School
template_name = 'basiccbv/school_detail.html'
# What i want is when I visit the link in the description I want to
# to see the school stuff and the form to add the student in this new
# view
class StudentCreateView(CreateView):
model = models.School
# I tried using the Student but that I don't know how to display the
# school information, I tried with related_name = 'students' but it
# didn't work(I don't know if that can be done the way that intended it
# or I just don't have the knowledge )
fields = ['name', 'age']
# If I use School I could get the name of the school in the title and
# its primary key, but than I don't know how to display the form and
# vise versa
template_name = 'basiccbv/student_update.html'
This is the .html file that gets me to the page where I need the form.
The link is the one calling 'basiccbv:studentupdate'
The related_name students was used here but I still can't figure out if it can
be done for adding stuff the way I want
<h1>Welcome to the school details page</h1>
<h2>School details:</h2>
<p>Name: {{ school_detail.name }}</p>
<p>Principal: {{ school_detail.principal }}</p>
<p>Location: {{ school_detail.location }}</p>
<h3>Students:</h3>
{% for student in school_detail.students.all %}
<p>{{ student.name }} who is {{ student.age }} years old.</p>
{% endfor %}
<div class="container">
<p><a href="{% url 'basiccbv:studentupdate' pk=school_detail.pk %}">Add a
student</a></p>
And here is the .html file with the form
## I changed this part bellow but nothing worked
<h1>Student coming to {{ student.school.name }}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Add student">
</form>
I'm really stuck and can't find any information about this but if you can help me or give any advice thank you.
The way I used to add students was with admin and for schools I used admin until I created the view for creating Schools which worked with no problems(probably because there were no foreign keys).
I think you can take this approach
Forms:
# We need to define a new form first
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ['name', 'age']
Views:
# we are using form view for using the form mentioned above
class StudentCreateView(FormView):
form_class = StudentForm
success_url = "/"
def get(self, request, school_id, **kwargs):
context = self.get_context_data(**kwargs) # getting context, ie: the form
context[school] = School.objects.get(pk=school_id) # updating the context with school object using the PK provided with the url
return self.render_to_response(context)
def post(self, request, school_id, **kwargs):
# overriding default implementation
form = self.get_form()
if form.is_valid():
return self.form_valid(form, school_id) # passing the pk value to form valid function to override
else:
return self.form_invalid(form)
def form_valid(self, form, school_id):
# overriding default implementation
self.object = form.save(commit=False)
self.object.school = School.objects.get(id=school_id) # saving the school information to the object
self.object.save()
return super(StudentCreateView, self).form_valid(form)
Template
# template
<h1>Student coming to {{ school.name }}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Add student">
</form>
Urls
path('school/student-update/<int:school_id>/', StudentCreateView.as_view(), name='studentupdate'),

Django form not validating when trying to update an existing row from database

I am working on my first Django project.
But I get following errors:
edit_file template
<form method="POST" action="{% url 'edit_file' file.id %}">
{% csrf_token %}
{{ form.errors }}
{{ form.non_field_errors }}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field.errors }}
{{ hidden_field }}
{% endfor %}
<div class="form-group row">
<label for="id_name" class="col-sm-3 col-form-label"> File Name </label>
<div class="col-sm-4">
{% render_field form.name|add_class:"form-control" %}
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">File Path</label>
<div class="col-sm-4">
{% render_field form.directory_path|add_class:"form-control" %}
</div>
</div>
<div class="form-group">
{% render_field form.script_code|add_class:"form-control" %}
<pre id="id_script_code" style="height: 40pc;">{{ form.script_code }}</pre>
</div>
<button type="submit" class="btn btn-success mr-2">Save Changes</button>
Back
</form>
Views.py
def edit_file(request, id):
instance = get_object_or_404(File, id=id)
if request.method == "POST":
form = EditFileForm(request.POST, instance=instance)
if form.is_valid():
print('Form validation => True')
form.save()
return HttpResponse('<h1> database updated! </h1>')
else:
print('Form validation => False')
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
else:
instance = get_object_or_404(File, id=id)
form = EditFileForm(request.POST or None, instance=instance)
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
forms.py
class EditFileForm(ModelForm):
# field_order = ['field_1', 'field_2']
class Meta:
print("forms.py 1")
model = File
fields = ('name', 'script_code', 'directory_path','version')
def clean(self):
print("forms.py 2")
cleaned_data = super(EditFileForm, self).clean()
name = cleaned_data.get('name')
print("cleaned data: ", cleaned_data)
Models:
Version id point to a version which contains multiple files.
class File(models.Model):
# Incrementing ID (created automatically)
name = models.CharField(max_length=40)
script_code = models.TextField() # max juiste manier?
directory_path = models.CharField(max_length=200)
version = models.ForeignKey('Version', on_delete=models.CASCADE)
class Meta(object):
db_table = 'file' # table name
class Version(models.Model):
# Incrementing ID (created automatically)
version_number = models.CharField(max_length=20)
pending_update = models.BooleanField(default=False)
creation_date = models.DateTimeField(auto_now_add=True, null=True, editable=False)
modification_date = models.DateTimeField(auto_now_add=True, null=True)
connecthor = models.ForeignKey('ConnecThor', on_delete=models.CASCADE)
def __str__(self):
return self.connecthor_id
The problem:
form.is_valid() keeps failing. My view returns one error.
*version: This field is required. But I don't know how to fix this. Users should only be able to update 3 of the 5 data fields. So there is no reason to show the PK or FK in the template.
You've included version in the list of fields in your form, but you aren't outputting it in the template so there is no means of providing it. Since the model field does not specify blank=True, it is a required field, hence the error.
If you don't want users to be able to modify this field, you should remove it from that list of fields under Meta.
You do not have version in your template. Your model field for version does not say it can have null values. Hence your form validation fails. Include it in your template or remove the field from EditFileForm class's Meta class in forms.py

Categories

Resources