Get ID of a ManyToManyField on ModelForm - django

I have a particular question envolving the editing of a model with a m2m field that i can't seem to find an answer anywhere. So i have a model, Equipment with an m2m relation to Tests.
models.py:
class Equipment(models.Model):
...
tests = models.ManyToManyField(Test, blank=True)
def __str__(self):
return '%s %s' % (self.manufacturer, self.model)
pass
forms.py:
class EquipmentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
modality = kwargs.pop('modality', None)
super (EquipmentForm, self).__init__(*args, **kwargs)
self.fields['tests'].widget = forms.CheckboxSelectMultiple()
self.fields['tests'].queryset = Test.objects.filter(modality=modality).order_by('group', 'number')
class Meta:
model = Equipment
fields = ['tests']
html:
...
<tbody>
{% for f in form.tests %}
<tr>
<td>{{ f }}</td>
<td>
<a data-toggle="modal" href="{% url 'app:modal' equipament_id=equipament_id test_id=f.id %}" data-target="#tests">See</a>
</td>
</tr>
{% endfor %}
</tbody>
As you can see i am trying to open a modal for each test field in the form so the user can see some information related to that test. This is where i am having trouble because i cant seem to get that f.id to pass it to a view that renders the modal dialog. Any ideas of how to do that? Please help.
First Attempt
{% for field in form.visible_fields %}
{% if field.name == "tests" %}
<table>
{% for choice in field.queryset %}
<tr>
<td><input value="{{choice.id}}" type="radio" name="tests" />{{choice.description}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endfor %}
This doesn't work because of the queryset. From the documentation it's not supported for a ModelMultipleChoiceField.
Second Attempt
<table>
<tbody>
{% for pk, choice in form.tests.field.choices %}
<tr>
<td>{{ choice }}</td>
<td><a data-toggle="modal" href="{% url 'SFM:modal' equipment_id=equipment_id test_id=pk %}" data-target="#tests">Ver</a></td>
</tr>
{% endfor %}
</tbody>
</table>
With this approach i am able to pass pk to the view and load the corresponding modal but i lost my form checkbox. If i manually render it i am not able to save any checkbox. This tells me that it is possible to pass the pk to view but maybe it should be acessible by querying the field form and not only in the choice. I am running out of ideas.

Related

Dynamically update table when creating new enty using HTMX

After recent success in some simple HTMX tasks I wanted to extend adamchainz django-htmx example by a modal field that updates a table dynamically. I am not sure if I am returning the right thing in render ... but my problem is, the table is not being updated. Only when I hit reload.
view:
class CreateProductView(TemplateView):
template_name = "app/product_create.html"
form_class = OrderForm
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {'form': self.form_class()})
def post(self, request):
Product.objects.create(name = request.POST["name"], price = request.POST["price"])
part_template = "app/partial-rendering.html"
return render(request, part_template, {"base_template": "app/_partial.html"})
urls.py:
path("create/", views.CreateProductView.as_view(), name = 'create'),
This is my index.html with the table in it:
<div class="..."><button class="btn btn-primary"
id="showButton"
hx-get="/create"
hx-target="#modal-create">create</button</div>
<main role="main" id="main">{% include "app/orders_table.html" %}</main>
<div id="modal-create"></div>
I also have a partial-rendering.html in place:
{% extends base_template %}
{% block main %}
{% include "app/product_table.html" %}
{% endblock %}
and a _partial.html:
<main id="main">
{% block main %}{% endblock %}
</main>
I will not post the whole product_table.html here, I guess it is straight forward ... mainly:
<table class="table table-striped table-hover">
<thead>
<tr>
<th>product name</th>
<th>price</th>
</tr>
</thead>
<tbody>
{% for product in page.object_list %}
<tr>
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The data from the form is collected via JS and sent to django using AJAX. I did not use the normal form submit because I wanted to avoid a page reload (which would solve that problem, I know).
I did try to put this together from the example mentioned above. Everything except the dynamic page update runs well!
Some approach is to remove the div targeted:
<div id="modal-create"></div>
then in your table body write the id deleted:
<tbody id="modal-create">
you want some partial for your table loops
{% for product in page.object_list %}
{% include 'some_table_body_tr_partial.html' %}
{% endfor %}
your partial might contain something like this:
<tr hx-target="this" hx-swap="outerHTML">
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
This approach have an issue: the width of the form will take the wide of the first column.

Django-tables2 send parameters to custom table template

I'm trying to use a custom table template to embed the django-filter fields on my table. So I copied the django-tables2 bootstrap.html template in a new file custom_table.html. Then I added it the following code in the thead section:
{% if filter %}
<tr>
{% for filter_field in filter.form.fields %}
<td>
{{ filter_field }}
</td>
{% endfor %}
<td>
<button class="login100-form-btn" type="submit">Filter</button>
</td>
</tr>
{% endif %}
So the problem is : how can I send the filter to the table template?
I resolved this issue. I have overridden the get_context_data function of my view:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
table = self.get_table(**self.get_table_kwargs())
table.filter = self.filterset
context[self.get_context_table_name(table)] = table
return context
In this way, I can use the filter in my custom table template by the following code:
{% if table.filter %}
<tr>
<form action="" method="get" class="form-inline">
{% csrf_token %}
{% for field_form in table.filter.form %}
<th>
{{field_form}}
</th>
{% endfor %}
<th>
<button class="btn" type="submit">Filter</button>
</th>
</form>
</tr>
{% endif %}
You can send the query paramters to the server, and construct the table using filtered_queryset. For example:
#views.py
def your_view(request):
filter = ModelFilter(request.GET, queryset=Model.objects.all())
filtered_queryset = filter.qs
# pass filtered_queryset to your table
table = SimpleTable(filtered_queryset)
return render(request, 'table.html', {'table': table})

How to obtain cleaned_data from a FormSetView (django-extra-views)

I cannot see how to trigger and obtain the cleaned_data from my FormSetView. With a django formset, I would call is_valid() on the formset within a POST'ed response, but not sure how to do that here. Examples in documentation do not show this, afaics.
I've implemented exactly as per the example in django-extra-views documentation. I already have a ModelFormSetView working fine although the DB update is automatic in that case. However, in this case of non-model implementation the cleaned data necessarily needs to be massaged into a different format for DB storage.
My view (called from the url entry):
class TemplateFSView(FormSetView):
template_name = 'template_season.html'
form_class = TemplateForm
success_url = 'tee/home/'
def get_initial(self):
# initial data is a pre-load test for now...
return [{'commence': '07:30', 'finish': '22:00', "spacing": "10"]
def formset_valid(self, formset):
# do whatever you'd like to do with the valid formset
print('How do I get here?')
return super(TemplateFSView, self).formset_valid(formset)
Form:
class TemplateForm(forms.Form):
commence = forms.CharField(required=True, label='first tee-time')
finish = forms.CharField(required=True, label='last tee-time')
spacing = forms.CharField(required=True, label='tee spacing')
Template:
<form method="post">
{% csrf_token %}
<table>
{% for form in formset %}
{% if forloop.counter == 1 %}
<thead>
<tr>
<th scope="col">{{ form.commence.label_tag }}</th>
<th scope="col">{{ form.finish.label_tag }}</th>
<th scope="col">{{ form.spacing.label_tag }}</th>
</tr>
</thead>
{% endif %}
<tr>
<td>{{ form.commence }}</td>
<td>{{ form.finish }}</td>
<td>{{ form.spacing }}</td>
</tr>
{% endfor %}
</table>
{{ formset.management_form }}
<input type="submit" value="Submit" />
</form>

Django dynamically filter list in template

I have a page which lists all of the athletes that a certain coach has. Coaches, however, can have multiple teams and I am trying to allow them to select a team from a dropdown at the top of the page and dynamically filter the list of athletes to only show those on the selected team.
My template:
<table class='table'>
<tr>
<td><h3>Team</h3></td>
<td><h3>First Name</h3></td>
<td><h3>Last Name</h3></td>
<td><h3>Email</h3></td>
</tr>
{% for athlete in athletes %}
{% if not athlete.coach_ind %}
<tr><td>
{% for team in athlete.team.all %}
{{ team.school }} {{ team.mascot }} {{ team.sport }}
{% endfor %}
</td>
<td>{{ athlete.user.first_name }}</td>
<td>{{ athlete.user.last_name }}</td>
<td>{{ athlete.user.email }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
My view:
teams_list = request.user.profile.team.all()
athletes = UserProfile.objects.filter(team__in=teams_list).order_by('team','user__last_name')
I am able to successfully get the correct list of all athletes and their information, I'm just not sure how to create a dynamic filter to show only by team.
You can use django-filter for it https://github.com/alex/django-filter.
Example from documentation:
Model
class Product(models.Model):
name = models.CharField(max_length=255)
manufacturer = models.ForeignKey(Manufacturer)
Filter
class ProductFilter(django_filters.FilterSet):
class Meta:
model = Product
fields = ['manufacturer']
View
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
return render_to_response('my_app/template.html', {'filter': f})
Template
{% block content %}
<form action="" method="get">
{{ filter.form.as_p }}
<input type="submit" />
</form>
{% for obj in filter %}
{{ obj.name }}<br />
{% endfor %}
{% endblock %}
Another app to those for this type of "chained" selection is selectable (http://django-selectable.readthedocs.io/en/latest/advanced.html#chained-selection). You have to put some additional javascript to the template, though.

Saving data from ModelForm

I am new to Django and I'm trying to save data using ModelForm. Template 'Vlozit' has a ModelForm and when submitted, I want the data saved in the DB and the redirect to base.html that actually loads the data from the DB and lists the output. The problem is that all works fine but the data is not saved. Please help me find what I am missing. Thank you.
Here's the model:
class Customer(models.Model):
Name = models.CharField(max_length=30)
Description = models.CharField(max_length=150)
Creation_Date = models.DateField(default=datetime.now)
Active = models.BooleanField(default=False)
def __unicode__(self):
return self.Name
Customers = models.Manager()
Here's the ModelForm:
class CustomerForm(forms.ModelForm):
Description = forms.CharField(widget=forms.Textarea)
class Meta:
model = Customer
Here's the view:
def vlozit(request):
if request.method == 'POST':
form = CustomerForm(request.POST, instance=Customer)
if form.is_valid():
form.save(True)
return HttpResponseRedirect(reverse('Base.html'))
else:
form = CustomerForm()
return render_to_response("Vlozit.html", {'form': form}, context_instance = RequestContext(request))
Here's the template 'Vlozit':
{% extends "base.html" %}
{% block head %}
{{ form.media }}
<script>
$(function() {
$( "#id_Creation_Date" ).datepicker();
});
</script>
{% endblock %}
{% block title %}{{block.super}} - Vlozit{% endblock %}
{% block content %}
<div id="content">
<form method="POST" action="{% url url_home %}">
{% csrf_token %}
<table>
<tr>
<td>Name</td>
<td>{{ form.Name }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ form.Description }}</td>
</tr>
<tr>
<td>Creation Date</td>
<td>{{ form.Creation_Date }}</td>
</tr>
<tr>
<td>Active</td>
<td>{{ form.Active }}</td>
</tr>
</table>
<input type="submit" value="Vlozit">
</form>
</div>
{% endblock content %}
As Timmy says in the comments, you don't catch the case where the form is not valid. Not only do you not show errors in the template, but you also don't even redisplay the template if form.is_valid() is False. Move the last line of the view back one indentation level, and add {{ form.errors }} to the template.