I am trying to edit manytomanyfield data. Here is my model
class Permission(models.Model):
shop = models.ForeignKey(Shop, on_delete=models.SET_NULL, null=True)
permission_title = models.CharField(max_length=255)
class Meta:
ordering = ["-id"]
def __str__(self):
return self.permission_title
class Roles(models.Model):
shop = models.ForeignKey(Shop, on_delete=models.SET_NULL, null=True)
role_title = models.CharField(max_length=255)
permissions = models.ManyToManyField(Permission)
class Meta:
ordering = ["-id"]
def __str__(self):
return self.role_title
Here in these models i have a model called permission. this model is in a manytomanyfield relation with Roles model. I want to edit this manytomanyfield. I have already did the creation part. But now i want to edit the data and I want to show the data in the templates, I chose while creating a role. I want to show it an input type checkox
Here is my views:-
def createRolesView(request, shop_id):
shopId = get_object_or_404(Shop, pk=shop_id)
permissions = Permission.objects.filter(
shop=shopId.id
)
if shopId.user == request.user:
if request.method == "POST":
role_title = request.POST.get("role_title")
shop = Shop.objects.get(id=shopId.id)
permissions = request.POST.getlist("permissions")
rl = Roles(
role_title = role_title,
shop = shop,
)
rl.save()
for p in permissions:
rl.permissions.add(p)
rl.save()
return redirect(f"/adminpanel/roles/{shopId.id}/")
args = {
"shopId": shopId,
"permissions": permissions,
}
return render(request, "roles/create-role.html", args)
else:
return redirect("warning")
def editRolesView(request, role_id, shop_id):
shopId = get_object_or_404(Shop, pk=shop_id)
roleId = get_object_or_404(Roles, pk=role_id)
if shopId.user == request.user:
if request.method == "POST":
roleId.role_title = request.POST.get("role_title")
shop = Shop.objects.get(id=shopId.id)
# roleId.permissions_set.all()
args = {
"shopId": shopId,
"roleId": roleId,
}
return render(request, "roles/edit-role.html", args)
else:
return redirect("warning")
Suggestions only, I haven't coded this myself.
[Edit. This question caused an "itch", so I coded it up to conform I was right. Skip ahead for a pretty generic edit M2M view ]
Two simple tables
One showing the current permissions assigned to the role, which has the queryset roles.permissions.all(), along with a "remove" checkbox for each.
The other showing the available permissions with an "add" checkbox for each. The queryset might be permissions.exclude( roles__pk=this_role.pk) (to exclude those already added, although it's harmless to add something already added). You should also exclude any permissions which this roles instance is not allowed to acquire.
You might use Django Formsets, but it's also rather easy to process raw data from request.POST in this case. Your template would basically be a form containing instances of
<input type="checkbox" name="add" value="{{permission.pk}}" >
and
<input type="checkbox" name="remove" value="{{permission.pk}}" >
with appropriate descriptive text gathered from the permission instance.
If it were me I'd display them as tables left and right, but as long as they are inside a <form> with Submit (and Done?) buttons, it's not relevant.
In your POST handling, you'd get the checked pk values with request.POST.getlist('add') and request.POST.getlist('remove'). For each one, revalidate that it was in the original queryset that was rendered into a form, and if the pk values are acceptable
role.permissions.add( permission) # or .remove( permission)
(where permission is the Permissions instance that's been revalidated)
After Submit of the above you'd probably redirect back to the same again so that the user can see that the requested additions and removals have happened. The "Done" button will redirect elsewhere( if "Done" in request.POST ... )
[Edit] The result of causing me an itch I had to scratch ....
class GenericEditM2MView( DetailView):
#model = Model # required as per DetailView
# template_name = # as per DetailView
#m2m_fieldname = None # no longer required if unique: the name of the model's m2m field to operate on
remove_qs = None # defaults to .all() of the m2m field
success_url = '.' # defaults to displaying the modified m2m relationship unless done
done_url = None # where to go if submit="Done", defaults to success_url
"""
template_name must define a form full of checkboxes, obtained from
{% for p in add_qs %}
<input type="checkbox" name="add" value="{{p.pk}}" > {% endfor}
{% for p in remove_qs %}
<input type="checkbox" name="remove " value="{{p.pk}}" > {% endfor}
default is to return to this same view after submit, to show that the changes
have been made. You can supply <input type="submit" name="submit" value="done" />
which will go to done_url instead of success_url
example use:
class PenStockM2MView( GenericEditM2MView):
template_name = 'playpen/edit_m2m.html'
model = PenStock
# m2m_fieldname = 'name' # works it out if ony one M2M field on the model
done_url = '/playpen/OK'
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
# everything works without this __init__ provided self.m2m_fieldname is present and correct.
# if model has only one m2m field, locate it via _meta as default.
# Also check m2m_fieldname is m2m because very confusing errors later if it's not!
f = getattr(self, 'm2m_fieldname', None)
m2m_fieldnames = [ field.name for field in self.model._meta.get_fields() if field.many_to_many ]
model_name = self.model.__name__
if f and not f in m2m_fieldnames:
raise AttributeError( f'field "{f}" is not a many-to-many field in {model_name}')
if not f:
if len( m2m_fieldnames ) == 1:
self.m2m_fieldname = m2m_fieldnames[0]
else:
raise AttributeError( f'Cannot identify a unique many-to-many field in {model_name}' )
def get_add_queryset(self):
field = getattr( self.object, self.m2m_fieldname)
remove_qs = self.get_remove_queryset()
already_there = remove_qs.values_list('pk', flat=True)
return remove_qs.model.objects.exclude( pk__in = already_there) # is qs.model documented?
def get_remove_queryset(self):
if hasattr( self, 'remove_queryset'):
return self.remove_qs
remove_qs = getattr( self.object, self.m2m_fieldname)
return remove_qs.all()
def get_context_data( self, **kwargs):
context = super().get_context_data( **kwargs)
context['add_qs'] = self.get_add_queryset()
context['remove_qs'] = self.get_remove_queryset()
return context
def post( self, request, *args, **kwargs):
self.object = self.get_object()
add = request.POST.getlist('add')
remove = request.POST.getlist('remove')
add_objs = list( self.get_add_queryset().filter(pk__in=add) )
remove_objs = list( self.get_remove_queryset().filter(pk__in=remove) )
field = getattr( self.object, self.m2m_fieldname )
field.add( *add_objs)
field.remove( *remove_objs)
return HttpResponseRedirect( self.get_done_url() or self.get_success_url() )
def get_success_url(self):
return self.success_url
def get_done_url( self):
done = self.request.POST.get("submit", None)
if done == "done" and hasattr(self, 'done_url'):
return self.done_url
return None
And here's a template (not completely generic. Using Bootstrap in base.html.)
<div class="row">
<div class="col-md-3 col-sm-4 col-xs-6">
<h1> Remove </h1>
<table class="table tbl">
{% for p in remove_qs %}
<tr><td>{{p.name }}</td><td><input type="checkbox" name="remove" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
{%endfor %}
</table>
</div>
<div class="col-md-3 col-sm-4 col-xs-6">
<h1> Add </h1>
<table class="table tbl">
{% for p in add_qs %}
<tr><td>{{p.name }}</td><td><input type="checkbox" name="add" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
{%endfor %}
</table>
</div>
</div>
<div class="row">
<form method="post" id="the-form"> {% csrf_token %}
<input type="submit" name="submit" value="submit" />
<input type="submit" name="submit" value="done" />
</form>
</div>
Related
I have a TextField intended to store a large amount of text that can be logically split into 10 parts. I thought that it would make sense to create 10 separate Textareas, each for one logical part. Thus, I subclassed MultiWidget and MultiValueField like so:
class MultiWidget(forms.widgets.MultiWidget):
template_name = "custom_content_widget.html"
attrs = {"class": "textarea form-control"}
def __init__(self, attrs=None):
widgets = [Textarea()] * 10
super(MultiWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return value
return ["", "", "", "", "", "", "", "", "", ""]
class ContentField(MultiValueField):
widget = MultiWidget
def __init__(self, *args, **kwargs):
# Define one message for all fields.
error_messages = {
'required': 'This field is required.',
}
# Or define a different message for each field.
fields = [CharField()] * 10
super(ContentField, self).__init__(
error_messages=error_messages, fields=fields, require_all_fields=True, *args, **kwargs)
# self.helper = FormHelper()
# self.helper.layout = Layout(
#
# )
def compress(self, data_list):
return " ".join(data_list)
with the custom_content_widget.html being just
{% for subwidget in widget.subwidgets %}
{% with widget=subwidget %}
{% include widget.template_name %}
{% endwith %}
{% endfor %}
Simple model and form in which I'd like to use this multiwidget
class Opinion(models.Model):
content = models.TextField()
class OpinionForm(forms.ModelForm):
content = ContentField()
class Meta:
model = Opinion
fields = ('__all__')
The problem is that when I use content in my form's HMTL as {{ form.content | as_crispy_field }} it renders pretty ugly
and I'd like all of the Textareas to be rendered one under the other. The main issue here is that textarea is rendered as
<textarea name="content_0" cols="40" rows="10" class="textarea" required id="id_content_0">
</textarea>
while "normal" TextField is rendered as
<textarea name="content" cols="40" rows="10" class="textarea form-control" required id="id_content">
</textarea>
and I have no clue how could I force the class of the widget to be textarea form-control instead of textarea. Initially, I found this question and also this blog but all they do is just to properly group widgets into rows and columns. Is there anything I am missing here?
The key was to pass attributes dict with "class": "textarea form-control" to the MultiWidget constructor as follows
class ContentField(MultiValueField):
widget = MultiWidget({"class": "textarea form-control"})
def __init__(self, *args, **kwargs):
# Define one message for all fields.
error_messages = {
'required': 'This field is required.',
}
# Or define a different message for each field.
fields = [CharField()] * 10
super(ContentField, self).__init__(
error_messages=error_messages, fields=fields, require_all_fields=True, *args, **kwargs)
def compress(self, data_list):
return " ".join(data_list)
I have created a coupon system where each user has 500 as balance initially in the employee table. I want to decrease this balance by the amount provided by user through the html forms. Given below are my files:
Models.py
from django.db import models
from django.contrib.auth.models import User
import django
import datetime
# Create your models here.
class vendor(models.Model):
id = models.CharField(max_length=20, primary_key=True)
name = models.CharField(max_length=30)
class employee(models.Model):
name = models.OneToOneField(User, on_delete=models.CASCADE)
id = models.CharField(max_length=20, primary_key=True)
balance = models.IntegerField(default=0)
class transaction(models.Model):
vendor_id = models.ForeignKey(vendor, on_delete=models.CASCADE)
emp_id = models.ForeignKey(employee, on_delete=models.CASCADE)
debit = models.IntegerField()
credit = models.IntegerField()
timestamp = models.DateField(("Date"), default=datetime.date.today)
views.py
def updatingBalance(request):
if request.method=="POST":
ven_id = request.POST["groupOfDefaultRadios"]
amount = request.POST["amt"]
x = employee.objects.get(name = request.user)
x.balance = x.balance - int(amount)
v = vendor.objects.get(id=ven_id)
w = employee.objects.get(id=x.id)
transaction.objects.create(vendor_id = v, emp_id=w,debit=amount,credit=0)
y = employee.objects.get(name = request.user)
#print(y.balance)
return render(request, 'profiles/userLogin.html', {'model':employee})
return render(request, 'profiles/userLogin.html')
html form:
{% if model %}
<h3>Balance amount is {{ model.balance }}</h3>
{% endif %}
<h3>Select vendor to pay!</h3>
<br>
<form method="POST" action="/profiles/userLogin/">
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" id="defaultGroupExample1" name="groupOfDefaultRadios" value="1">
<label class="custom-control-label" for="defaultGroupExample1">Vendor 1</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" id="defaultGroupExample2" name="groupOfDefaultRadios" value="2">
<label class="custom-control-label" for="defaultGroupExample2">Vendor 2</label>
</div>
<input type="" class="form-control" id="amount1" name="amt" aria-describedby="emailHelp" placeholder="Enter amount">
<br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Where am I going wrong? How to refer to the employee model directly? I understand that x might not be referencing to the model correctly. Also, I need to show the balance on html. How can that be achieved?
The reason this does not work is because you never .save() your Employee object. Furthermore you should redirect in case of a succesful POST request to implement the Post/Redirect/Get pattern [wiki].
from django.shortcuts import redirect
def updatingBalance(request):
if request.method=="POST":
ven_id = request.POST["groupOfDefaultRadios"]
amount = request.POST["amt"]
x = employee.objects.get(name=request.user)
x.balance = x.balance - int(amount)
x.save()
v = vendor.objects.get(id=ven_id)
w = employee.objects.get(id=x.id)
transaction.objects.create(vendor_id=v, emp_id=w, debit=amount, credit=0)
return redirect(updatingBalance)
y = employee.objects.get(name = request.user)
return render(request, 'profiles/userLogin.html', {'model': y})
That being said, I would strongly advise not to store the balance in the Employee model. It will generate a lot of trouble like race conditions to keep the balance correctly updated.
Instead you can make an annotation that calculates for the employee the balance with:
class EmployeeManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
balance=Sum(F('transaction__credit')-F('transaction__debit'))
)
class Employee(models.Model):
name = models.OneToOneField(User, on_delete=models.CASCADE)
id = models.CharField(max_length=20, primary_key=True)
# no balance
objects = EmployeeManager()
Here we will thus calculate an aggregate over the related Transaction objects.
In that case the view is thus:
from django.shortcuts import redirect
def updatingBalance(request):
if request.method=="POST":
ven_id = request.POST["groupOfDefaultRadios"]
amount = request.POST["amt"]
x = employee.objects.get(name=request.user)
v = vendor.objects.get(id=ven_id)
transaction.objects.create(vendor_id=v, emp_id=x, debit=amount, credit=0)
return redirect(updatingBalance)
y = employee.objects.get(name=request.user)
return render(request, 'profiles/userLogin.html', {'model': y})
So we do not need to update a balance field, it is simply updated by adding a transaction.
Note: normally a Django models, just like all classes in Python are given a name in PerlCase, not snake_case, so it should be: Employee instead of employee.
Note: when you pass an object to the template, that is not a model, it is a model object. You thus might want to change the name of the variable from 'model' to 'employee'.
Is it possible to create multiple objects for a model in django by looping the same form in a for loop. I am using a custom model form.
My template is:
{% for query in queryset %}
<form method="POST" action="{% url 'academics' %}" style=" padding: 5%">
{% csrf_token %}
<input type="text" name="Student" class="form-control" id="id_Student"
value="{{query}}">
<input type="text" name="Subject" class="form-control" required id="id_Subject">
<input type="checkbox" name="Presence" id="id_Presence">
<button type="Submit" id="submit">Submit</button>
{% endfor %}
<button type="Submit" id="submit">Submit</button>
</form>
My models.py is:
class Attendance(models.Model):
Student = models.CharField(max_length=100, blank=False)
Hour = models.CharField(max_length=1, blank=False)
Subject = models.CharField(max_length=8, blank=False)
Date = models.DateTimeField(default=timezone.now)
Presence = models.BooleanField(default=False, blank=False)
def __str__(self):
return f'{self.Student}'
My views.py is:
def academics(request):
if request.user.is_staff:
form = forms.AttendanceForm()
context = {
'form': form,
'queryset': User.objects.filter(profile__Year='SY',profile__Department='CSE')
}
if request.method == "POST" :
form = forms.AttendanceForm(request.POST)
if form.is_valid():
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student,Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
return render(request, 'console/academics.html',context)
Currently i can create multiple objects, but with the same values of the last form. ie, the object is created with the values of last form. Here i have looped the form so that n number of forms will be generated for n queries with the name filled automatically in the first field. I know explaining this is little complex. Anyone can help?
I'm not entirely clear what you mean by "looping a form", but if you want the user to be able to enter a list of arbitrary length of similar sets of data, then what you want is a Formset or a ModelFormset. When it comes back, you validate all the data that he has submitted, and if it's all good then you iterate through it, usually creating or modifying multiple objects.
Due to reputation I'm unable to comment but I believe this is how you achieve your desired result. by using WHILE LOOP.
I myself have not much knowledge of python & Django but I guess this is the logic. Please correct me if I am wrong instead of down voting.
var = 0
n = 5
if request.method == "POST":
form = forms.AttendanceForm(request.POST)
if form.is_valid():
while var < n:
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student, Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
var += 1
return render(request, 'console/academics.html', context)
The Problem
I'm trying to make a project, where you can make topics, that can be private or public to unauthenticated users. In every topic, you can then make several entries, applying to that topic. Now I'm trying to make a checkbox in my new_topic.html, where if you check it, it evaluates to True, if not, to False. But I'm having trouble with making the checkbox evaluate to True, when I check it. And for some reason, there is two checkbuttons in the page that applies to new_topic.html; one with a label, and one with just a box.
What I've tried
I've tried recreating the db.sqlite3 database. But that didn't work.
I've tried using request.POST.get('public', False) in my new_topic() function, when saving the new_topic variable.¨
The Code
My learning_logs/models.py looks like this:
from django.db import models
from django.contrib.auth.models import User
class Topic(models.Model):
"""A topic the user is learning about."""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
public = models.BooleanField(default=False)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
"""Return a string representation of the model."""
return self.text
class Entry(models.Model):
"""Something specific learned about a topic."""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Return a string representation of the model."""
# Add an ellipsis ONLY if the entry,
# is more than 50 characters long.
if self.text > self.text[:50]:
return self.text[:50] + "..."
elif self.text <= self.text[:50]:
return self.text[:50]
My learning_logs\views.py looks like this:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, Http404
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
def index(request):
"""The Home Page for Learning Log."""
return render(request, 'learning_logs/index.html')
def check_topic_owner(request, topic):
"""Checks if the topic requested, is requested by the owner.
Else return Http404.
"""
if topic.owner != request.user:
raise Http404
#login_required
def topics(request):
"""Show all topics."""
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
#login_required
def topic(request, topic_id):
"""Show a single topic and all its entries."""
topic = get_object_or_404(Topic, id=topic_id)
# Make sure the Topic belongs to the current user.
check_topic_owner(request, topic)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
#login_required
def new_topic(request):
"""Add a new topic."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
new_topic = form.save(commit=False)
new_topic.owner = request.user
new_topic.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
#login_required
def new_entry(request, topic_id):
"""Add a new entry for the particular topic."""
topic = get_object_or_404(Topic, id=topic_id)
check_topic_owner(request, topic)
if request.method != 'POST':
# No data submitted; create a blank form.
form = EntryForm()
else:
# POST data submitted; process data.
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
if new_entry.topic.owner == request.user:
new_entry.save()
else:
return Http404
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic_id]))
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
#login_required
def edit_entry(request, entry_id):
"""Edit an existing entry."""
entry = get_object_or_404(Entry, id=entry_id)
topic = entry.topic
check_topic_owner(request, topic)
if request.method != 'POST':
# Initial request; pre-fill form with the current entry.
form = EntryForm(instance=entry)
else:
# POST data submitted; process data.
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic.id]))
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html', context)
My learning_logs\forms.py looks like this:
from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols': 80})}
My learning_logs\templates\learning_logs\new_topic.html looks like this:
{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}
{% block header %}
<h2>New topic:</h2>
{% endblock header %}
{% block content %}
<h1>Add a new topic:</h1>
<form action="{% url 'learning_logs:new_topic' %}" method='post'
class="form">
{% csrf_token %}
{% bootstrap_form form %}
<div class="form-check">
<input class="form-check-input" type="checkbox" value=True id="public">
<label class="form-check-label" for="public">
Make it public?
</label>
</input>
</div>
{% buttons %}
<button name="submit" class="btn btn-primary">Add Topic</button>
{% endbuttons %}
</form>
{% endblock content %}
I just can't seem to fix this error. Thanks in advance! Any help is appreciated.
You have:
<input class="form-check-input" type="checkbox" value=True id="public">
<label class="form-check-label" for="public">
Make it public?
</label>
</input> # <<< this closing tag is wrong
correct input tag :
<input class="form-check-input" type="checkbox" value=True id="public" />
So you'll have:
<label class="form-check-label" for="public">
Make it public?
</label>
<input class="form-check-input" type="checkbox" value=True id="public" />
Forms.py
class CheckPostedForm(forms.ModelForm):
posted = forms.BooleanField(required=False, initial=False)
Views.py
postform = CheckPostedForm(request.POST, instance=author)
if postform.is_valid():
postform.save()
else:
print 'Cannot save post form.'
Models.py
posted = models.CharField(max_length=2, blank=True, null=True)
class Meta:
model = TmpPlInvoice
exclude = ['net_amt', 'post_date', 'address', 'particulars', 'pos_code', 'acct', 'cust', 'voucher_id', 'voucher_date', 'post_date']
labels = {
'posted': 'Posted',
}
I want a checkbox which is initially unchecked and based on th value from DB i check/uncheck it. No matter what i do the form always comes out as checked="checked".
print postform gives this,
<tr><th><label for="id_posted">Posted:</label></th><td><input checked="checked" id="id_posted" name="posted" type="checkbox" value="n" required /></td></tr>
I have searched alot every where, the docs say that it should be initially false required=False if we want to apply conditions.
Detail
Views.py
def master_detail_posted(request):
if request.method == 'GET':
author = TmpPlInvoice.objects.get(id=1)
postform = CheckPostedForm(instance=author)
return render(request,'main.html' ,{'postform': postform})
if request.method == 'POST':
author = TmpPlInvoice.objects.get(id=1)
postform = CheckPostedForm(request.POST, instance=author)
print postform
if postform.is_valid():
logger.info('saving post form %s', postform.cleaned_data)
postform.save()
else:
logger.info('post form is not valid %s %s', postform.errors, request.POST)
return render(request,'main.html' ,{'postform': postform})
Template
<div> {{ postform }} </div>
You've hard coded the field in the template. Clearly, Django can't change HTML tags that you've written explicitly.
You need to use Django tags in your template:
<td>{{ form.posted }}</td>