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

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>

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.

display number of views for each post for a specific author

i want to print number of views for a specific post for e.g barack obama = 3 albert einstein = 1 elon musk = 0 that's it,
but it print multiple values for each entries as you can see in the image.
so how to achieve this. should i have to use dictionary or what ?
enter image description here
models.py
class ObjectViewed(models.Model):
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
ip_address = models.CharField(max_length=220, blank=True, null=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # User, Blog, or any other models
object_id = models.PositiveIntegerField() # User id, Blog id, or any other models id
content_object = GenericForeignKey('content_type', 'object_id')
timestamp = models.DateTimeField(auto_now_add=True)
views.py
class PostListView(ListView):
model = Post
template_name = 'edmin/post/postList.html'
context_object_name = 'posts'
ordering_by = ['-created']
def get_queryset(self):
post=Post.objects.filter(author=self.request.user)
return post
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
post=Post.objects.filter(author=self.request.user)
c_type = ContentType.objects.get_for_model(Post)
context['count_view'] = [ObjectViewed.objects.filter(content_type=c_type, object_id=p.id) for p in post ]
print(context['count_view'])
return context
postList.html
<table id="table_id" class="table display" border='2' align="center">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Banner Title</th>
<th scope="col">Created</th>
<th scope="col">Number of Views</th>
<th scope="col">Status</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody style="color:#000">
{% for post in posts %}
{% if post.status == 'Draft' %}
{% else %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td><a style="color:blue" href="{% url 'edmin:post_detail_view' pk=post.pk %}">{{ post.title }}</a></td>
<td>{{ post.banner_title }}</td>
<td>{{ post.created }}</td>
<td>
{% for c in count_view %}
{{ c|length }}
{% endfor %}
</td>
<td>{{ post.status }}</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
This is the problem of your code:
<td>
{% for c in count_view %}
{{ c|length }}
{% endfor %}
</td>
I believe count_view is the list of all the view count across objects? If that's the case, the event in your screen will happen because you displayed all items in the count_view list. You might need to change this part to:
<td>
{{ count_view.forloop.counter }}
</td>
You need to get the view for a specific post, not to print out every single one of them. You might need to check as I don't know where your list starts, but this is briefly how this works.
Research

How to render a model in table form, and select some of them using checkbox using django?

I have a model called ItemBatch
# item upload
class ItemBatch(models.Model):
ttypes =(('Open','Open'),('Container','Container'),('Trailer','Trailer'),('All','All'))
uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='uploaded_by')
name = models.CharField(max_length=30)
pid = models.IntegerField(blank=True)
quantity = models.IntegerField(blank=True)
length = models.FloatField(blank=True)
width = models.FloatField(blank=True)
height = models.FloatField(blank=True)
volume = models.FloatField(blank=True)
weight = models.FloatField(blank=True)
truck_type = models.CharField(max_length=255,default=0, choices=ttypes)
origin = models.CharField(max_length=100, blank=True)
destination = models.CharField(max_length=100, blank=True)
time = models.DateTimeField(max_length=100, blank=True,default=now)
rtd = models.BooleanField(default=False) #ready to dispatch checkbox
def __str__ (self):
return self.name
And I am using this views function to render it:
#method_decorator([login_required, teacher_required], name='dispatch')
class UploadedItems(ListView):
model = ItemBatch
ordering = ('name',)
context_object_name = 'items'
template_name = 'classroom/teachers/item_list.html'
def get_queryset (self):
return ItemBatch.objects.filter(uploaded_by=self.request.user)
I am rendering this table in a template and getting this:
This is the code in the template:
{% for quiz in last %}
<tr>
<form method="post" novalidate>
{% csrf_token %}
<td class="align-middle"><input type="checkbox" value="{{ quiz.pid }}"></td>
<td class="align-middle">{{ quiz.name }}</td>
<td class="align-middle">{{ quiz.pid }}</td>
<td class="align-middle">{{ quiz.quantity }}</td>
<td class="align-middle">{{ quiz.length }}x{{ quiz.width }}x{{ quiz.height }}</td>
<td class="align-middle">{{ quiz.volume }}/{{ quiz.weight }}</td>
<td class="align-middle">{{ quiz.origin }}</td>
<td class="align-middle">{{ quiz.destination }}</td>
<td class="align-middle">{{ quiz.time|naturaltime }}</td>
</form>
</tr>
{% empty %}
What I tried
As you can see, I have created a form inside the table and also included a checkbox. But, I am not able to take the output of the that checkbox in any way. How can I let the user select a few items, and get that data returned to me in any way so I can use it another function ? If the checkbox can just give me the id or pk value, I will be able to reverse it with the appropriate object, but I can't create an input form on a table which already is an output render of another function. Is this the correct way ?
You could add a submit button at the end of your form, and inside the form tag specify the action and map it to a django view.
Also, you need to moove out your form tags outside of the for loop.
Should look like this:
<form method="post" action="{% url 'your_url_name'%}" novalidate>
{% csrf_token %}
{% for quiz in last %}
<tr>
<td class="align-middle"><input name='quiz-pids' id='checkbox-{{forloop.counter}}' type="checkbox" value="{{ quiz.pid }}"></td>
<td class="align-middle">{{ quiz.name }}</td>
<td class="align-middle">{{ quiz.pid }}</td>
<td class="align-middle">{{ quiz.quantity }}</td>
<td class="align-middle">{{ quiz.length }}x{{ quiz.width }}x{{ quiz.height }}</td>
<td class="align-middle">{{ quiz.volume }}/{{ quiz.weight }}</td>
<td class="align-middle">{{ quiz.origin }}</td>
<td class="align-middle">{{ quiz.destination }}</td>
<td class="align-middle">{{ quiz.time|naturaltime }}</td>
</tr>
{% endfor %}
<input type="submit" value='Do something'>
</form>
In you urls add to path:
path('your-url', views.YourView.as_view(), name='your_url_name'),
Your View:
class YourView(View):
def post(self, request, *args, **kwargs):
#get the selected quizs
quizs = request.POST.getlist('quiz-pids')
#retrieves thoses quizes from the database:
items = ItemBatch.objects.filter(pid__in=quizs)
#do something with each of them:
for item_batch in items:
#do something
pass
There are two things wrong here.
Firstly you need a single form around all of the options, not one form per row.
And secondly you need a name attribute in your input element, so that the browser knows how to send it to the backend.
<form method="post" novalidate>
{% csrf_token %}
{% for quiz in last %}
<tr>
<td class="align-middle"><input type="checkbox" name="pids" value="{{ quiz.pid }}"></td>
...
</tr>
{% endfor %}
<input type="submit">
</form>
Now in your view you can get the submitted values via self.request.POST.getlist('pids') - for instance, you could use that to filter the queryset via ItemBatch.objects.filter(pid__in=self.request.POST.getlist('pids')).

Get ID of a ManyToManyField on ModelForm

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.

problem using django's User authentication model. Uncomprehensible error

I want to register users.
So I made a:
class RegisterForm(ModelForm):
class Meta:
model=User #(from django.auth.contrib.User)
now i display that form in a template for the users to fill it and to submit it.
When i do form.is_valid(), i get this validation error: Your username and password didn't match. Please try again.
Does anyone know what can cause this?
i've included the ModelForm, the view that processes the registration form and the template that displays the registration form.
Thank you for your time and kind concern.
MY MODEL FORM
class RegisterForm(ModelForm):
class Meta:
model=User
THE VIEW THAT HANDLES THE REGISTRATION
def register(request):
if request.method=='POST':
form=RegisterForm(request.POST)
if form.is_valid(): # HERE I GET A VALIDATION ERROR
new_user=User.objects.create_user(username=form.cleaned_data['username'],
password=form.cleaned_data['password'],
email=form.cleaned_data['email'],
)
new_user.is_active=True
new_user.first_name=form.cleaned_data['first_name']
new_user.last_name=form.cleaned_data['lastname']
return HttpResponseRedirect(reverse('confirm_registered'),args=[form.cleaned_data['username']])
else:
return render(request,'login/register.html',{'form':form})
else:
raise ValueError()
THE REGISTRATION TEMPLATE
{% extends "store/index2.html" %}
{% block canvas %}
<h3>Registration</h3>
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="/retailstore/login/register.html">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
<tr>
<td>{{ form.first_name.label_tag }}</td>
<td>{{ form.first_name }}</td>
</tr>
<tr>
<td>{{ form.last_name.label_tag }}</td>
<td>{{ form.last_name }}</td>
</tr>
<tr>
<td>{{ form.email.label_tag }}</td>
<td>{{ form.email }}</td>
</tr>
</table>
<input type="submit" value="register" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}
The form is not validating, but your template is set to always show the same error message about username and password not matching:
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
The reason it is not validating is probably because you are only using some of the fields from the User object, and a ModelForm expects all of the non-editable fields. In the Form's Meta, set
fields = 'username', 'password', 'first_name', 'last_name', 'email'
to tell it to only require those fields.
Also note that passwords are not stored in the database as plain text, so creating a user with a password like that will not work. You need to use user.set_password(form_password).
As Dave points out, you always show the same message no matter what the validation error. You should do this:
{% if form.errors %}
{{ form.errors }}
{% endif %}