I'm trying to incorporate Select2 into my django form -- specifically ModelSelect2MultipleWidget -- so that the user can associate multiple event objects to another model (like CheckboxSelectMultiple). The associated models are:
from django.contrib.auth import get_user_model
from django.db import models
class F4Events(models.Model):
name = models.CharField(max_length=300)
handling_type = models.ManyToManyField(WaferHandlingType)
def __str__(self):
return self.name
class ToolPm(models.Model):
ceid = models.ForeignKey(CEID, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
wafer_handling = models.BooleanField(default=True)
wafer_based = models.BooleanField(default=False)
frequency = models.FloatField(blank=True, null=True)
handling_type = models.ManyToManyField(WaferHandlingType, blank=True)
user_edited = models.BooleanField(default=False)
pm_f4_events = models.ManyToManyField('F4Events', blank=True)
def __str__(self):
return str(self.name) if self.name else ''
class Meta:
ordering = ('ceid', 'name')
My forms.py file is:
from django import forms
from .models import Entity, ToolPm, CEID, PDL, F4Events, WaferHandlingType
from django_select2 import forms as s2forms
class F4EventWidget(s2forms.ModelSelect2MultipleWidget):
search_fields = [
'name__icontains',
]
attrs = {'data-placeholder': 'Please Choose F4 Events'}
class PMForm(forms.ModelForm):
class Meta:
model = ToolPm
fields = ['wafer_handling', 'wafer_based', 'handling_type', 'pm_f4_events']
widgets = {'handling_type': forms.CheckboxSelectMultiple,
'pm_f4_events': F4EventWidget
}
my view.py is:
from datetime import datetime, timedelta
from django.db.models import Sum
from django.http import JsonResponse, HttpResponseRedirect
from django.http.response import HttpResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.views import View
from django.views.generic import ListView
from django_filters.views import FilterView
from pages.filter import CeidFilter, PmRunDateFilter
from tools.forms import EntityForm, PMForm, CEIDForm, PDLAddForm
from tools.models import CEID, Entity, F4Events, ToolPm, WaferHandlingType, PmRunDate
from django.urls import reverse
class UserCeidPMUpdate(View):
template_name = 'tools/update_pm_attributes.html'
def get(self, request, username, pk, pm_id):
pm = ToolPm.objects.get(pk=pm_id)
form = PMForm(request.GET or None, instance=pm)
return render(request, self.template_name, {'form': form, 'pm': pm, })
def post(self, request, username, pk, pm_id):
pm = ToolPm.objects.get(pk=pm_id)
pm.user_edited = True
pm.save()
form = PMForm(request.POST or None, instance=pm)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('pm_view', kwargs={'username': username, 'pk': pm.ceid.id}))
return render(request, self.template_name, {'form': form, 'pm': pm, })
lastly, my .html file is:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">
<p>Process: {{ pm.ceid.process }}</p>
<p>CEID: {{ pm.ceid.ceid }}</p>
<p>Name: {{ pm.name }}</p>
<p>Frequency: {{ pm.frequency }}{% if pm.wafer_based %} wafers {% else %} days {% endif %}</p>
<p>Score: {{ pm.score }}</p>
{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-success" value="Update PM"><button type="button" class="btn btn-primary">Go Back</button>
</form>
{% endblock content %}
If I switch the pm_f4_events widget to a CheckboxSelectMultiple or a ModelSelect2Widget, the code works.
However, when I try to use a ModelSelect2MultipleWidget, the form continually tries to load but cannot and eventually just times out.
There are ~5000 items within the F4Events model, so that may have something to do with it. Any help to point me in the right direction would be greatly appreciated!
You can empty the pm_f4_events field's choices in the __init__ method:
class PMForm(forms.ModelForm):
class Meta:
model = ToolPm
fields = ['wafer_handling', 'wafer_based', 'handling_type', 'pm_f4_events']
widgets = {
'handling_type': forms.CheckboxSelectMultiple,
'pm_f4_events': s2forms.ModelSelect2MultipleWidget(
model=F4Events,
search_fields=["name__icontains",],
attrs = {'data-placeholder': 'Please Choose F4 Events'}
)
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
choices_F4Events = []
pm_f4_events_field = self.fields["pm_f4_events"]
# selected values
selected_F4Events = self.data.getlist("pm_f4_events") or self.initial.get("pm_f4_events")
if selected_F4Events:
# check of selected values not object
if not isinstance(selected_F4Events[0], F4Events):
selected_F4Events = pm_f4_events_field.queryset.filter(pk__in=selected_F4Events)
choices_F4Events = [(item.pk, item.name) for item in selected_F4Events]
# assign selected values or empty list
pm_f4_events_field.choices = choices_F4Events
Has been tested on my server, works well.
Related
I am trying to update my ActiveMember model which has a OneToOneField with Members. I am running into this error.
Reverse for 'update' with keyword arguments '{'id': ''}' not found. 1 pattern(s) tried: ['update/(?P<id>[0-9]+)/\\Z']
I am new to django and not sure how to solve it.
forms.py
from dataclasses import fields
from random import choices
from socket import fromshare
from django import forms
from django.forms import ModelForm, DateField, widgets
from .models import Member, ActiveMember
from datetime import datetime, timedelta
class MemberForm(ModelForm):
class Meta:
model = Member
fields = (
'full_name',
'email',
'phone',
'image',
'date_created',
)
def __init__(self, *args, **kwargs):
super(MemberForm, self).__init__(*args, **kwargs)
self.fields['image'].required = False
self.fields['email'].required = False
self.fields['date_created'].disabled = True
class ActiveMemberForm(ModelForm):
class Meta:
model = ActiveMember
fields = (
'member',
'start_date',
'end_date',
'status',
)
widgets = {
'start_date': widgets.DateInput(attrs={'type': 'date'}),
'end_date': widgets.DateInput(attrs={'type': 'date'}),
}
models.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
import os
from django.db import models
from django.utils import timezone
from datetime import datetime, timedelta
from django.utils.text import slugify
from .helper import upload_to_image_post
# Create your models here.
def default_start_time():
#now = datetime.now()
now = timezone.now()
start = now.replace(hour=23, minute=59, second=0, microsecond=0)
return start if start > now else start + timedelta(days=1)
class Something(models.Model): timestamp = models.DateTimeField(default=default_start_time)
class Member(models.Model):
full_name = models.CharField(max_length=125)
email = models.EmailField(max_length=125, blank=True, null=True)
phone = models.CharField(max_length=20)
image = models.ImageField(max_length= 256, upload_to=upload_to_image_post, null=True, blank=True)
date_created = models.DateTimeField(default=timezone.now())
class Meta:
verbose_name_plural = "All Members"
def __str__(self):
return str(f"{self.full_name}")
class ActiveMember(models.Model):
member = models.OneToOneField(Member, on_delete=models.CASCADE, related_name='is_member')
start_date = models.DateField(default=timezone.now())
end_date = models.DateTimeField(default=default_start_time)
status = models.CharField(max_length=2, choices=(('1','Active'), ('2','Inactive')), default = '0', blank=True, null=True)
def __str__(self):
return str(f"{self.member}")
views.py
def update(request, id): #this function is called when update data
member = Member.objects.get(pk=id)
form = ActiveMemberForm(request.POST, instance=member)
if form.is_valid():
# the `form.save` will also update your newest image & path.
form.save()
return render(request, 'members/update.html', {
'form': form,
'success': True
})
else:
member = Member.objects.get(pk=id)
form = ActiveMemberForm(instance=member)
return render(request, 'members/update.html', {
'form': form
})
urls.py
from django.urls import path
from . import views
app_name = 'members'
urlpatterns = [
path('', views.index, name='index'),
path('<int:id>', views.view_member, name='view_member'),
path('add/', views.add, name='add'),
path('edit/<int:id>/', views.edit, name='edit'),
path('delete/<int:id>/', views.delete, name='delete'),
path('active/', views.active, name='active'),
path('inactive/', views.inactive, name='inactive'),
path('activate/', views.activate, name='activate'),
path('update/<int:id>/', views.update, name='update'),
]
update.html
{% extends 'members/layout.html' %}
{% block body %}
<h3 class="text-center m-4">Update Membership</h3>
{% if success %}
<div class="alert alert-success" role="alert">
The Membership has been updated successfully.
Go to Home Page.
</div>
{% else %}
<div class="row justify-content-center">
<div class="col-6">
<div class="card bg-light mb-3">
<div class="card-header">
<i class="fa-solid fa-pen-to-square fa-lg"></i> Update Membership
</div>
<div class="card-body">
<form action="{% url 'members:update' id=member.id %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Update</button>
Cancel
</form>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
Also, I am trying to make a gym management system where I can add new members and activate their membership, and view active member and inactive members (see photo below for what I currently have). I would love some input if I am heading in the right direction. After, I can solve my current problem. I would like to tackle a big obstacle. I would like the members after their membership expires the date is over their status changes to inactive (automatically) so I can view them in the inactive section.
I was referencing Member model instead of ActiveMember hopefully this will help someone.
def update(request, id):
member = ActiveMember.objects.get(pk=id)
if request.method == 'POST':
form = ActiveMemberForm(request.POST, instance=member)
if form.is_valid():
# update the existing `Band` in the database
form.save()
# redirect to the detail page of the `Band` we just updated
return render(request, 'members/update.html', {
'form': form,
'success': True,
})
else:
form = ActiveMemberForm(instance=member)
return render(request, 'members/update.html', {
'form': form,
'member': member,
})
What I wish to achieve is to "disable" the possibility for a user to update or delete a record after a set period of time. In my application, a user creates a delivery booking slot, as part of which they specify a delivery date delivery_date.
Requirement: A user should not be able to update/delete a booking slot within 7 days of the delivery_date
How would I go about this?
models.py
from django.db import models
from django.utils import timezone
from django.urls import reverse
from django.contrib.auth.models import User
class Customer(models.Model):
username = models.ForeignKey(User,on_delete=models.CASCADE)
name = models.CharField(max_length=20,null=True)
def __str__(self):
return self.name
# Create your models here.
class Booking(models.Model):
customer_name = models.ForeignKey(Customer,on_delete=models.CASCADE,null=True)
username = models.ForeignKey(User,on_delete=models.CASCADE)
qty_plts = models.PositiveSmallIntegerField(default=1)
cbm = models.PositiveSmallIntegerField(default=1)
created_date = models.DateTimeField(default=timezone.now())
delivery_date = models.DateField(null=True)
delivery_time = models.TimeField(null=True)
booking_number = models.CharField(max_length=50,unique=True)
def __str__(self):
return self.booking_number
def save(self, **kwargs):
if not self.booking_number:
self.booking_number = f"{self.delivery_date:%Y%m%d}{self.delivery_time:%H%M}"
super().save(**kwargs)
def get_absolute_url(self):
return reverse('bookmyslot:detail',kwargs={'pk':self.pk})
views.py
from django.shortcuts import render
# Create your views here.
from .models import Booking,Customer
from .forms import BookingForm
from django.urls import reverse,reverse_lazy
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import (ListView,DetailView,CreateView,UpdateView,DeleteView,TemplateView)
class AboutView(TemplateView):
template_name = 'about.html'
class HomeView(TemplateView):
template_name = 'index.html'
class BookingCreate(LoginRequiredMixin,CreateView):
login_url = '/login'
redirect_field_name = 'bookmyslot/booking_detail.html'
model = Booking
form_class = BookingForm
def get_form_kwargs(self, **kwargs):
form_kwargs = super(BookingCreate,self).get_form_kwargs(**kwargs)
form_kwargs['username'] = self.request.user
return form_kwargs
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.username = self.request.user
self.object.save()
return super().form_valid(form)
class BookingList(LoginRequiredMixin,ListView):
login_url = '/login'
model = Booking
template_name = 'bookmyslot/booking_list.html'
context_object_name = 'bookings'
def get_queryset(self):
if not self.request.user.is_staff:
return Booking.objects.filter(username=self.request.user)
else:
return Booking.objects.all()
class BookingDetail(LoginRequiredMixin,DetailView):
login_url = '/login'
context_object_name = 'booking_detail'
model = Booking
def get_queryset(self):
if not self.request.user.is_staff:
return Booking.objects.filter(username=self.request.user)
else:
return Booking.objects.all()
class BookingUpdate(LoginRequiredMixin,UpdateView):
login_url = '/login'
template_name = 'bookmyslot/booking_form_update.html'
model = Booking
form_class = BookingForm
def get_form_kwargs(self, **kwargs):
form_kwargs = super(BookingUpdate,self).get_form_kwargs(**kwargs)
form_kwargs['username'] = self.request.user
return form_kwargs
class BookingDelete(LoginRequiredMixin,DeleteView):
login_url = '/login'
model = Booking
success_url = reverse_lazy('bookmyslot:list')
forms.py
from django import forms
from bookmyslot.models import Booking,Customer
from bootstrap_datepicker_plus import DatePickerInput
import datetime as dt
from django.utils import timezone
from .utilities import check_free_time
import os
HOUR_CHOICES = [(dt.time(hour=x), '{:02d}:00'.format(x)) for x in range(7, 13)]
class BookingForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
user = kwargs.pop('username',None)
super(BookingForm,self).__init__(*args,**kwargs)
self.fields['qty_plts'].label = "Quantity Of Pallets"
self.fields['cbm'].label = "Shipment CBM"
self.fields['delivery_date'].label = "Delivery Date"
self.fields['delivery_time'].label = "Delivery Time"
self.fields['customer_name'].label = "Customer Name"
self.fields['customer_name'].queryset = Customer.objects.filter(username=user)
def clean(self):
cleaned_data = super(BookingForm,self).clean()
cleaned_delivery_date = cleaned_data.get('delivery_date')
cleaned_delivery_time = cleaned_data.get('delivery_time')
booking_number = f"{cleaned_delivery_date:%Y%m%d}{cleaned_delivery_time:%H%M}"
if Booking.objects.filter(booking_number=booking_number).exists():
x = Booking.objects.filter(delivery_date=cleaned_delivery_date)
occupied_slots = []
for i in range(0,len(x)):
occupied_slots.append(x[i].booking_number[-4:])
all_slots = ['{:02d}00'.format(x) for x in range(7, 13)]
available_slot = check_free_time(all_slots,occupied_slots)
if available_slot:
message = f"Requested slot is already booked, here are today's available times: {os.linesep.join(available_slot)}"
raise forms.ValidationError(message)
else:
message = "There are no more slots available today, please choose another date"
raise forms.ValidationError(message)
class Meta:
model = Booking
fields = ('customer_name','qty_plts','cbm','delivery_date','delivery_time')
widgets = {'delivery_date':DatePickerInput(options={"daysOfWeekDisabled":[0,6],"minDate":timezone.now().date().strftime('%Y-%m-%d')}),
'delivery_time':forms.Select(choices=HOUR_CHOICES)}
urls.py
from . import views
from django.urls import path
app_name = 'bookmyslot'
urlpatterns = [
path('mybookings/',views.BookingList.as_view(),name='list'),
path('mybookings/<int:pk>/',views.BookingDetail.as_view(),name='detail'),
path('update/<int:pk>/',views.BookingUpdate.as_view(),name='update'),
path('delete/<int:pk>/',views.BookingDelete.as_view(),name='delete'),
path('new/',views.BookingCreate.as_view(),name='create'),
path('',views.HomeView.as_view(),name='home'),
path('about/',views.AboutView.as_view(),name='about')
]
booking_detail.html
{% extends 'bookmyslot/base.html' %}
{% block content %}
<div class="jumbotron">
<h2>Booking Number: {{ booking_detail.booking_number }}</h2>
<h3>Details:</h3>
<p></p>
{% if user.is_staff %}
<p>Company Name: {{booking_detail.username}}</p>
<p>Customer Name: {{ booking_detail.customer_name }}</p>
<p>Quantity Of Pallets: {{ booking_detail.qty_plts }}</p>
<p>CBM Of Delivery: {{ booking_detail.cbm }}</p>
<p>Delivery Date: {{ booking_detail.delivery_date }}</p>
<p>Delivery Time: {{ booking_detail.delivery_time }}</p>
{% else %}
<p>Customer Name: {{ booking_detail.customer_name }}</p>
<p>Quantity Of Pallets: {{ booking_detail.qty_plts }}</p>
<p>CBM Of Delivery: {{ booking_detail.cbm }}</p>
<p>Delivery Date: {{ booking_detail.delivery_date }}</p>
<p>Delivery Time: {{ booking_detail.delivery_time }}</p>
{% endif %}
</div>
<div class="container">
<a class="btn btn-primary" href="{% url 'bookmyslot:update' pk=booking_detail.pk %}">Update Booking</a>
<a class="btn btn-danger" href="{% url 'bookmyslot:delete' pk=booking_detail.pk %}">Delete Booking</a>
</div>
{% endblock %}
You can use a dynamic #property to check if the booking is at less than 7 days from delivery date :
models.py
from datetime import date, timedelta
class Booking(models.Model):
# ...
#property
def can_be_altered(self):
"""Return True if delivery_date - 7 days >= 0 else return False"""
return self.delivery_date - date.today() >= timedelta(7)
booking_detail.html
<!-- Disable button if can not be altered -->
<!-- You can replace the button with a warning message too -->
<div class="container">
<a class="btn btn-primary" {% if booking_detail.can_be_altered is not True %} disabled {% endif %} href="{% url 'bookmyslot:update' pk=booking_detail.pk %}">Update Booking</a>
<a class="btn btn-danger" {% if booking_detail.can_be_altered is not True %} disabled {% endif %} href="{% url 'bookmyslot:delete' pk=booking_detail.pk %}">Delete Booking</a>
{% if booking_detail.can_be_altered is not True %}
<small class="form-text text-muted text-warning">You can not delete or update this booking within 7 days before delivery date </small>
{% endif %}
</div>
I'm new to Django, so any help is appreciated. I have one class Gym and a second class Route (rock climbing gym and climbing routes). Each gym can contain multiple routes, but each route can only belong to one gym. I can list available gyms and click on one to go to the gym page, but I want to list all of the routes that belong to it and can't figure out how.
# /gym/models.py
from django.db import models
from django.shortcuts import reverse
class Gym(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField(upload_to='gym', default='default_route.jpg')
address = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse('gym:detail', kwargs={'pk': self.pk})
def __str__(self):
return self.name
# route/models.py
from django.db import models
from .utils import generate_qrcode
class Route(models.Model):
Gym = models.ForeignKey('gym.Gym', on_delete=models.CASCADE, null=True)
grade = models.CharField(max_length=10)
hold_color = models.CharField(max_length=20, default='')
rating = models.PositiveIntegerField()
date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='routes', default='default_route.jpg')
def __str__(self):
return str(self.pk)
# gym/views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView
from .models import Gym
from route.models import Route
def gym_list_view(request):
# Can filter this for specific gyms
qs = Gym.objects.all()
return render(request, 'gym/index.html', {'gym_list': qs})
def gym_detail_view(request, pk):
gym_obj = Gym.objects.get(pk=pk)
# This is where I don't know how to get the routes that belong to the current gym
routes_obj = Route.objects.get(pk=pk)
return render(request, 'gym/detail.html', {'gym_object': gym_obj, 'routes_obj': routes_obj})
# gym/templates/gym/detail.html
{% extends "base.html" %}
{% block title %}
{{ gym_object.pk }}
{% endblock title %}
{% block content %}
Gym ID: {{gym_object.pk }} <br><br>
<h3> {{ gym_object.name }} </h3>
{{ gym_object.address }}
<br><br>
<h4>Routes</h4>
route_obj: {{ routes_obj }}
{% endblock content %}
def gym_detail_view(request, pk):
gym_obj = Gym.objects.get(pk=pk)
# This is where I don't know how to get the routes that belong to the current gym
routes = gym_obj.route_set.all()
return render(request, 'gym/detail.html', {'gym_object': gym_obj, 'routes': routes})
Try this
I am trying to add comment to an Ad posted by a user. When I press the comment button, it is showing the error.
Here's my models.py
from django.db import models
from django.core.validators import MinLengthValidator
from django.contrib.auth.models import User
from django.conf import settings
class Ad(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Title must be greater than 2 characters")]
)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
text = models.TextField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#picture
picture = models.BinaryField(null=True, blank=True, editable=True)
content_type = models.CharField(max_length=256, null=True, help_text='The MIMEType of the file')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
comments = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Comment', related_name='comments_owned')
#show up in the admin list
def __str__(self):
return self.title
class Comment(models.Model) :
text = models.TextField(
validators=[MinLengthValidator(3, "Comment must be greater than 3 characters")]
)
ad = models.ForeignKey(Ad, on_delete=models.CASCADE)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Shows up in the admin list
def __str__(self):
if len(self.text) < 15 :
return self.text
return self.text[:11] + ' ...'
Here's my forms.py
# commentForm
from django import forms
from ads.models import Ad, Comment
from django.core.files.uploadedfile import InMemoryUploadedFile
from ads.humanize import naturalsize
from django.core import validators
from django.core.exceptions import ValidationError
...
class CommentForm(forms.Form):
comment = forms.CharField(required=True, max_length=500, min_length=3, strip=True)
class Meta:
model = Comment
fields = ['text']
Here's my views.py
from ads.models import Ad, Comment
from ads.owner import OwnerListView, OwnerDetailView, OwnerCreateView, OwnerUpdateView, OwnerDeleteView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.views import View
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.http import HttpResponse
from ads.forms import CreateForm, CommentForm
class AdDetailView(OwnerDetailView):
model = Ad
def get(self, request, pk):
a = Ad.objects.get(id=pk)
comments = Comment.objects.filter(ad=a).order_by('-updated_at')
comment_form = CommentForm()
context = {'ad': a, 'comments':comments, 'comment_form':comment_form}
return render(request, 'ads/ad_detail.html', context)
....
class CommentCreateView(LoginRequiredMixin, View):
def post(self, request, pk):
ad = get_object_or_404(Ad, id=pk)
comment = Comment(text=request.POST['comment'], owner=request, ad=ad)
comment.save()
return redirect(reverse('ads:ad_detail', args=[pk]))
class CommentDeleteView(OwnerDeleteView):
model = Comment
def get_success_url(self):
ad = self.object.ad
return reverse('ads:ad_detail', args=[ad.id])
And finally my templates ad_detail.html
{% if user.is_authenticated %}
<br clear="all"/>
<p>
{% load crispy_forms_tags %}
<form method="post" action="{% url 'ads:ad_comment_create' ad.id %}">
{% csrf_token %}
{{ comment_form|crispy }}
<input type="submit" value="Submit">
<input type="submit" value="All Ads" onclick="window.location.href='{% url 'ads:all' %}'; return false;">
</form>
</p>
{% endif %}
{% for comment in comments %}
<p> {{ comment.text }}
({{ comment.updated_at|naturaltime }})
{% if user == comment.owner %}
<i class="fa fa-trash"></i>
{% endif %}
</p>
{% endfor %}
After clicking on the submit button, it's supposed to be taking to ads:all but instead throwing an error.
In case you also want to check urls.py
urlpatterns = [
path('', views.AdListView.as_view()),
path('ads', views.AdListView.as_view(), name='all'),
...
#comments
path('ad/<int:pk>/comment', views.CommentCreateView.as_view(), name='ad_comment_create'),
path('comment/<int:pk>/delete', views.CommentDeleteView.as_view(success_url=reverse_lazy('ads')), name='ad_comment_delete'),
]
You should be using ModelForm instead of Form here.
class CommentForm(forms.ModelForm):
comment = forms.CharField(required=True, max_length=500, min_length=3, strip=True)
class Meta:
model = Comment
fields = ['text']
And change your view
class CommentCreateView(LoginRequiredMixin, View):
def post(self, request, pk):
form = CommentForm(request.POST)
ad = get_object_or_404(Ad, id=pk)
comment = form.save(commit=False)
comment.ad = ad
comment.owner = request.user
comment.save()
return redirect('ads:ad_detail', ad.pk)
In you CommentCreateView, your owner is recieving request that was the reason you were shown that error. So:
comment = Comment(text=request.POST['comment'], owner=request.user, ad=ad)
I am new to coding and especially new to working with Django. I want to build a small "forum" for me and the other students and it works pretty fine.
There is just one error I encounter over and over:
Exception Type: NoReverseMatch
Exception Value: Reverse for 'thread-create' with
arguments '('',)' not found. 1 pattern(s) tried: ['forum/(?P<pk>[0-9]+)/add/$']
I can work around it using functions instead of class based Views, but I doubt that's how it's supposed to be done.
I have the feeling I am missing something very important while working with class views.
I really would appreciate if someone could help me out :-)
It's the links that are not working:
{% url 'user-posts' thread.author.username %}
and more importantly:
{% url 'forum:thread-create' thread.id %}
What's also not working while using classes is:
{% for thread in thema.thread_set %} (works with the functions)
(I only want to see the threads that are bind to a certain thema. That also works pretty well with functions).
And you might also be asking why I don't just use the function based views:
I want to know what am I doing wrong
I want to be able to easy paginate my site.
Code:
# models.py
from django.db import models
from django.urls import reverse
from PIL import Image
from django.utils import timezone
from django.contrib.auth.models import User
class Thema(models.Model):
themengebiet = models.CharField(max_length=350)
beschreibung = models.TextField()
themen_logo = models.FileField(max_length=350, upload_to="logos", default='default.jpg')
erstellt_am = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.themengebiet
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.themen_logo.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.themen_logo.path)
class Thread(models.Model):
thema = models.ForeignKey(Thema, on_delete=models.CASCADE)
titel = models.CharField(max_length=350)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
erstellt_am = models.DateTimeField(default=timezone.now)
thread_logo = models.ImageField(upload_to="logos", d efault='default.jpg')
def get_absolute_url(self):
return reverse('forum:thread-page', kwargs={'pk': self.thema.id})
def __str__(self):
return self.titel
class Posting(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
titel = models.CharField(max_length=350)
erstellt_am = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
inhalt = models.TextField()
def __str__(self):
return self.titel
views.py
from django.views import generic
from django.views.generic import CreateView, UpdateView, DeleteView, ListView
from .models import Thema, Thread, Posting
from django.shortcuts import render, get_object_or_404
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.core.paginator import Paginator
class ThemenView(ListView):
template_name = 'forum/posts_original.html'
context_object_name = 'all_themen'
def get_queryset(self):
return Thema.objects.all()
class ThemaCreate(CreateView):
model = Thema
fields = ['themengebiet', 'beschreibung', 'themen_logo']
success_url = "/forum/"
class ThreadCreate(CreateView):
model = Thread
fields = ['thema', 'titel', 'erstellt_am']
success_url = reverse_lazy('forum:posts_original')
def get_object(self):
return Thread.objects.get(pk=self.kwargs['pk'])
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostCreate(CreateView):
model = Posting
fields = ['thread', 'titel', 'inhalt', 'erstellt_am']
success_url = "/forum/"
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ThreadView(ListView):
model = Thread
context_object_name = 'threads'
template_name = 'forum/thread.html'
success_url = reverse_lazy('forum:posts_original')
def get_object(self):
return Thread.objects.get(pk=self.kwargs['pk'])
class PostView(ListView):
model = Posting
context_object_name = 'posts'
template_name = 'forum/posts.html'
urls.py
from django.urls import path
from . import views
app_name = 'forum'
urlpatterns = [
# /forum/
path('', views.ThemenView.as_view(), name='posts_original'),
# forum/id/
path('<int:pk>/', views.ThreadView.as_view(), name='thread-page'),
# forum/thread/add
#path('<int:pk>/add/', views.thread_create, name='thread-create'),
# forum/id//id
path('thread/<int:pk>/', views.PostView.as_view(), name='post-page'),
path('add/', views.ThemaCreate.as_view(), name='thema-create'),
path('<int:pk>/add/', views.ThreadCreate.as_view(), name='thread-create'),
path('thread/<int:pk>/add/', views.PostCreate.as_view(), name='post-create'),
]
'''
def thread(request, pk):
try:
thema = get_object_or_404(Thema, pk=pk)
except Thema.DoesNotExist:
raise Http404("Thema existitiert nicht")
return render(request, 'forum/thread.html', {'thema': thema})
def post(request, pk):
try:
thread = get_object_or_404(Thread, pk=pk)
except Thread.DoesNotExist:
raise Http404("Thema existitiert nicht!")
return render(request, 'forum/posts.html', {'thread': thread})
'''
{% extends "blog/base.html" %}
{{ threads }}
{% block content %}
Zurück zur Themenübersicht
{% for thread in threads %}
<article class="media content-section">
<img class="rounded-circle article-img" src= "{{ thread.thread_logo.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' thread.author.username %}">{{ thread.author }}</a>
<small class="text-muted">{{ thread.erstellt_am|date:"d. F Y" }}</small>
</div>
<h2><a class="article-title" href=" {% url 'forum:post-page' thread.id %}">{{ thread.titel }}</a></h2>
<p class="article-content">Thema: {{ thread.thema }}</p>
</div>
</article>
{% endfor %}
<form action="{% url 'forum:thread-create' thread.id %}" method="post" enctype="multipart/form-data" style="display: inline;">
{% csrf_token %}
Thread hinzufügen
</form>
{% endif %}
{% endblock content %}