All Foreign Keys,Booleans and Choices fields are not rendering on the form template.But their label tags are rendering.
For example:
This is a "Generated TimeTable" model of a school system.
The model
class GT(models.Model):
timetable_id = models.CharField(primary_key=True,max_length=200)
period_number = models.ForeignKey(Ref_Period,on_delete=models.CASCADE)
scheduled_date_and_time = models.ForeignKey(Ref_Calendar,on_delete=models.CASCADE)
schedule_id = models.ForeignKey(Planned_Timetable,on_delete=models.CASCADE)
subject = models.ForeignKey(Ref_Subject,on_delete=models.CASCADE)
teacher_id = models.ForeignKey(Teacher,on_delete=models.CASCADE)
def __str__(self):
return str(self.teacher_id)
I already imported the in the forms.py file
forms.py
class GTForm(ModelForm):
class Meta:
model = GT
fields = '__all__'
View:
#login_required(login_url = 'main:loginPage')
#allowed_users(allowed_roles=['admin'])
def dalltt(request):
gtform = GTForm()
if request.method == "POST":
gtform = GTForm(request.POST)
print(gtform)
if gtform.is_valid():
gtform.save()
print("saved")
return redirect('/')
else:
print(gtform.errors)
print(gtform)
context = {'gtform':gtform}
return render(request=request,template_name="main/dalltimetable.html",context=context)
The template(dalltimetable.html):
<form enctype="multipart/form-data" action="" method="POST" >
{% csrf_token %}
<table>
<tr>
<th>ID</th>
<th>Time</th>
<th>Day</th>
<th>Subject</th>
<th>{{ gtform.teacher_id.label_tag }} </th>
</tr>
<tr>
<td>{{ gtform.teacher_id }} </td>
<td>{{ gtform.teacher_id }}</td>
</tr>
</table>
<input class="btn btn-warning" type="submit" value="Update">
{{ form.name.errors }}
{{ form.non_field_errors }}
</form>
Give this a try:
#login_required(login_url = 'main:loginPage')
#allowed_users(allowed_roles=['admin'])
def dalltt(request):
if request.method == "POST":
gtform = GTForm(request.POST)
print(gtform)
if gtform.is_valid():
gtform.save()
print("saved")
return redirect('/')
else:
print(gtform.errors)
else:
gtform = GTForm()
return render(request, 'main/dalltimetable.html', {'gtform': gtform })
Also maybe try switching to class-based views to help with the boilerplate:
class dalltt(LoginRequiredMixin, FormView):
template_name = 'main/dalltimetable.html'
form_class = GTForm
success_url = '/'
login_url = 'main:loginPage'
def form_valid(self, form):
form.save()
return super().form_valid(form)
def form_invalid(self, form):
print(form.errors)
Related
I have previously figured out how to send and save notifications by many to many fields to send to users, now i can't specifically call the notifications by current user logged in i.d.
Models.py
class Notifications(models.Model):
id=models.AutoField(primary_key=True)
sent_to = models.ManyToManyField(CustomUser)
message = models.TextField(null=True)
message_reply = models.TextField(null=True)
created_at=models.DateTimeField(auto_now_add=True)
updated_at=models.DateTimeField(auto_now=True)
The notifications_sent_to is the created table after I created the many to many save
Notifications_sent_to table (which is not created in the models)
Notifications table (which stores the message)
Views.py
def add_notification(request):
notifs = Notifications.objects.all()
users = CustomUser.objects.filter(is_staff=True)
if request.method == 'POST':
form = AddNotifForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.message_reply = "none"
instance.save()
form.save_m2m()
sent_to = form.cleaned_data.get('sent_to')
messages.success(request, f'Message has been successfully sent .')
return redirect('add_notif')
else:
form = AddNotifForm()
context={
'notifs' : notifs,
'form' : form,
'users' : users,
}
template_name ='main-admin/add-notif.html'
return render(request, template_name, context)
Forms.py
class AddNotifForm(forms.ModelForm):
sent_to = forms.ModelMultipleChoiceField(
queryset=CustomUser.objects.filter(is_staff=True).exclude(is_superuser=True),
widget=forms.CheckboxSelectMultiple,
required=True)
class Meta:
model = Notifications
fields = ['sent_to', 'message']
Templates view
{% for sent_to_users in sent_to_users %}
<tr>
<td>{{ sent_to_users.id }}</td>
<td>{{ sent_to_users.sent_to }}</td>
<td>{{ sent_to_users.message }}</td>
<td>
{% if sent_to_users.message_reply == "none" %}
<button type="button" class="btn btn-primary reply_open_modal" data-bs-toggle="modal" data-bs-target="#replyModal">Reply</button>
{% else %}
{{ sent_to_users.message_reply }}
{% endif %}
</td>
<td>{{ sent_to_users.created_at }}</td>
</tr>
{% endfor %}
Picture gets uploaded on the django admin panel but when i click on the image on the panel it shows the page not found error.
forms.py
class ApproveImgForm(forms.ModelForm):
class Meta:
model = ApprovImg
fields = ['photo']
urls.py
path('w_p.html', views.WProduct_list, name='WProduct_list'),
views.py
def WProduct_list(request, category_slug=None):
category = None
categories = Category.objects.all()
wproducts = Product.objects.filter()
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
wproducts = Product.objects.filter()
if(request.method=='POST'):
form = ApproveImgForm(request.POST, request.FILES)
form.save()
context = {
'category': category,
'categories': categories,
'wproducts': wproducts,
}
return render(request, 'shop/w_p.html', context)
models.py
class ApprovImg(models.Model):
photo=models.ImageField(upload_to='products/%Y/%m/%d')
def __str__(self):
return str(self.photo)
w_p.html
<tr>
<td>{{ product.name }}</td>
<td> {{ product.price }}</td>
<td><form action="w_p.html" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit">
</form>
</td>
</tr
Can someone please help?
You should inherit from ModelForm.
class ApproveImgForm(forms.ModelForm):
class Meta:
model = ApprovImg
fields = "__all__" # not recommended, you should specify the fields.
# views.py
def upload_file(request):
if request.method == 'POST':
form = ApproveImgForm(request.POST, request.FILES)
if form.is_valid():
# file is saved
form.save()
return HttpResponseRedirect('/home/')
else:
form = ApproveImgForm
return render(request, 'upload_image.html', {'form': form})
# urls.py
urlpatterns = [path('upload', upload_file, name='upload')]
# upload_image.html
<form action="{% url 'upload' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.photo.label_tag }} {{ form.photo.help_text }}</p>
<p>
{{ form.photo.errors }}
{{ form.photo }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
I have been trying to update image since I added an ImageField to model.py but it always gives me this error The 'image' attribute has no file associated with it.
From Admin panel given by Django I can do so I can add and update, but I need to create my own way of updating images but id doesn't work.
Here are my attachments
This is my model
class rooms(models.Model):
room_number = models.PositiveIntegerField(unique = True)
room_type = models.ForeignKey(room_types, on_delete=models.CASCADE)
number_of_beds = models.PositiveIntegerField()
image = models.ImageField(upload_to = 'room_pics/', null = True, blank = True)
price = models.PositiveIntegerField(default=0)
class Meta:
verbose_name_plural = 'Rooms'
def __str__(self):
room_number = "Room number: " + str(self.room_number)
return room_number
Here is a form from forms.py
class addRoomForm(forms.ModelForm):
class Meta:
model = rooms
fields = ['room_number', 'room_type', 'number_of_beds', 'price', 'image']
and here are the views from views.py to add and update
def add_room(request):
form = addRoomForm()
if request.method == 'POST':
form = addRoomForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, f'Room added successfully!')
return redirect('add_room')
else:
form = addRoomForm()
context = {
'form' : form,
}
myTemplate = 'hotel/addRoom.html'
return render(request, myTemplate, context)
def update_room(request, id):
instance = get_object_or_404(rooms, pk = id)
form = addRoomForm(request.POST or None, instance = instance)
if form.is_valid():
form.save()
messages.success(request, f'Room has been updated successifully!')
return redirect ('view_rooms')
context = {
'form': form,
}
myTemplate = 'hotel/addRoom.html'
return render(request, myTemplate, context)`
and the template source code is
<div class=" w3-card">
<table class="table table-hover table-bordered">
<thead>
{% if room.image.url %}
<tr>
<img src="{{room.image.url}}" alt="image" width="711px" height="400px;">
</tr>
{% endif %}
</thead>
<tbody>
<tr>
<td>Room number</td>
<td>{{room.room_number}}</td>
</tr>
<tr>
<td>Room Type</td>
<td>{{room.room_type}}</td>
</tr>
<tr>
<td>Number of beds</td>
<td>{{room.number_of_beds}}</td>
</tr>
<tr>
<td>Price</td>
<td>{{room.price}}</td>
</tr>
<tr>
<td><i class="fa fa-edit"></i>
<i class="fa fa-trash"></i>
</td>
</tr>
</tbody>
</table>
</div>
It should be form = addRoomForm(request.POST, request.FILES)
See File Uploads, Here is detailed and good documented tutorial for you.
I have a CBV that use ListView at first here my views.py
class InventoryListView(ListView):
context_object_name = 'inventorys'
model = models.Inventory
and here my template_list.html
{% for inventory in inventorys %}
<tr>
<td>{{ inventory.name }}</td>
<td>{{ inventory.sn }}</td>
<td>{{ inventory.employee.name }}</td>
<td>{{ inventory.desc }}</td>
</tr>
{% endfor %}
it returns all the data as expected.
but I need add form with it. and then add some of code to my views.py
class InventoryListView(ListView):
template_name ='system/inventory_list.html'
context_object_name = 'inventorys'
model = models.Inventory
def get(self, request):
form = InventoryForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = InventoryForm(request.POST)
and here my forms.py
class InventoryForm(forms.ModelForm):
name = forms.CharField(max_length=255)
sn = forms.DecimalField(max_digits=20, decimal_places=0)
desc = forms.CharField(widget=forms.Textarea)
employee = forms.ModelChoiceField(queryset=Employee.objects.all(), to_field_name="id")
class Meta:
model = Inventory
fields = ('name', 'sn', 'desc', 'employee')
and here my template_list.html
{% for inventory in inventorys %}
<tr>
<td>{{ inventory.name }}</td>
<td>{{ inventory.sn }}</td>
<td>{{ inventory.employee.name }}</td>
<td>{{ inventory.desc }}</td>
</tr>
{% endfor %}
<form method="post" action="{% url 'system:inventory_create' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
now form working perfectly and checked on my DB its submitted. but list of data not showing like before, because I add:
def get(self, request):
form = InventoryForm()
return render(request, self.template_name, {'form': form})
to my views.py
so how to make both works, list of data & form.
Try to avoid overriding get or post for generic class-based-views. It's easy to end up duplicating existing functionality or having to repeat code.
In this case, you can add the form to the template context by overriding the get_context_data method.
class InventoryListView(ListView):
template_name ='system/inventory_list.html'
context_object_name = 'inventorys'
model = models.Inventory
def get_context_data(self, **kwargs):
context = super(InventoryListView, self).get_context_data(**kwargs)
context['form'] = InventoryForm()
return context
...
Send the form through get_context_data() method:
def get_context_data(self, **kwargs):
context = super(InventoryListView,self).get_context_data(**kwargs)
context['form'] = InventoryForm()
return context
I have a similar situation. I tried many things, and now using ListView with FormMixin.
First, I make FormListView inheriting ListView and FormMixin. Here's my code
from django.http import Http404
from django.views.generic import ListView
from django.views.generic.edit import FormMixin
from django.utils.translation import ugettext as _
class FormListView(FormMixin, ListView):
def get(self, request, *args, **kwargs):
# From FormMixin
form_class = self.get_form_class()
self.form = self.get_form(form_class)
# From ListView
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty and len(self.object_list) == 0:
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
% {'class_name': self.__class__.__name__})
context = self.get_context_data(object_list=self.object_list, form=self.form)
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
As you see, I made my own get for FormMixin andListView` both.
And inheriting this, I make my own SomeFormListView, In your case InventoryFormListView.
class InventoryFormListView(FormListView):
template_name ='system/inventory_list.html'
context_object_name = 'inventorys'
model = models.Inventory
# FormListView
form_class = YourCustomModelForm
# your custom method
To save the state of the form
def get_context_data(self, **kwargs):
context = super(RecentChannels, self).get_context_data(**kwargs)
context['form'] = RecentChannelsForm(self.request.GET)
return context
In my formset I would like to check each reading against a target if the reading is larger than the target do not save to db. For some reason I can't get this to work correctly because it still allows me to save. Any help would be greatly appreciated!
all in views.py
#custom formset validation
def get_dim_target(target):
dim = Dimension.objects.values_list('target', flat=True).filter(id=target)
return dim
#custom formset validation
class BaseInspecitonFormSet(BaseFormSet):
def insp_clean(self):
if any(self.errors):
return
reading = []
for form in self.forms:
dim_id = form.cleanded_data['dimension_id']
reading = form.cleaned_data['reading']
target = get_dim_target(dim_id)
if reading > target:
raise forms.ValidationError("Reading larger than target")
reading.append(reading)
#formset
def update_inspection_vals(request, dim_id=None):
dims_data = Dimension.objects.filter(id=dim_id)
can_delete = False
dims = Dimension.objects.get(pk=dim_id)
sheet_data = Sheet.objects.get(pk=dims.sheet_id)
serial_sample_number = Inspection_vals.objects.filter(dimension_id=24).values_list('serial_number', flat=True)[0]
target = Dimension.objects.filter(id=24).values_list('target', flat=True)[0]
title_head = 'Inspect-%s' % dims.description
if dims.ref_dim_id == 1:
inspection_inline_formset = inlineformset_factory(Dimension, Inspection_vals, can_delete=False, extra=0, fields=('reading',), widgets={
'reading': forms.TextInput(attrs={'cols': 80, 'rows': 20})
})
if request.method == "POST":
formset = inspection_inline_formset(request.POST, request.FILES, instance=dims)
if formset.is_valid():
new_instance = formset.save(commit=False)
for n_i in new_instance:
n_i.created_at = datetime.datetime.now()
n_i.updated_at = datetime.datetime.now()
n_i.save()
else:
form_errors = formset.errors
formset.non_form_errors()
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
else:
formset = inspection_inline_formset(instance=dims, queryset=Inspection_vals.objects.filter(dimension_id=dim_id).order_by('serial_number'))
return render(request, 'app/inspection_vals.html',
{
'formset': formset,
'dim_data': dims_data,
'title':title_head,
'dim_description': dims.description,
'dim_target': dims.target,
'work_order': sheet_data.work_order,
'customer_name': sheet_data.customer_name,
'serial_sample_number': serial_sample_number,
})
inspection_val.html
<h1>Inspeciton Values</h1>
<div class="well">
<form method="post">
{% csrf_token %}
<table>
{{ formset.management_form }}
{% for x in formset.forms %}
<tr>
<td>
Sample Number {{ forloop.counter0|add:serial_sample_number }}
</td>
<td>
{{ x }}
{{ x.errors }}
</td>
</tr>
{% endfor %}
</table>
<input type="submit" value="Submit Values" class="btn-primary" />
</form>
</div>
</div>
The Django docs on custom formset validation show that you should create a clean method.
You have named your method insp_clean, so Django will never call it. Rename the method to clean.