I am setting up my CategoryDetailView for my CRM. Then this error occurred:
'CategoryDetailView' object has no attribute 'get_object'
here's my code sectrion from views.py:
class CategoryDetailView(LoginRequiredMixin, generic.ListView):
template_name = "clients/category/category_detail.html"
context_object_name = "category"
def get_context_data(self, **kwargs):
context = super(CategoryDetailView, self).get_context_data(**kwargs)
clients = self.get_object().client_set.all()
context.update({
"clients": clients
})
return context
Here's my models.py
class Client(models.Model):
first_name = models.CharField(max_length=30)
middle_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
mobile_number = models.CharField(max_length=12)
organization = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
agent = models.ForeignKey("Agent", null=True, blank=True, on_delete=models.SET_NULL)
category = models.ForeignKey("Category", related_name="clients", null=True, blank=True, on_delete=models.SET_NULL)
class Category(models.Model):
name = models.CharField(max_length=30) # New, Tapped, Active, Closed
organization = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
def __str__(self):
return self.name
Thanks in advance!
You are working with a ListView [Django-doc], not a DetailView [Django-doc], and a ListView indeed has no .get_object(…) method [Django-doc]. You furthermore should specify a model = … or queryset = … to specify with what queryset the DetailView is dealing.
You thus should inherit from the DetailView. You can also work with self.object to prevent making an extra query:
from django.views.generic import DetailView
class CategoryDetailView(LoginRequiredMixin, DetailView):
template_name = 'clients/category/category_detail.html'
context_object_name = 'category'
model = Category
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
clients = self.object.client_set.all()
context['clients'] = clients
return context
There is als no reason to add this to the context, in the template, you can simply work with:
{% for client in category.client_set.all %}
{{ client }}
{% endfor %}
Related
I have created a form named as AttendanceForm :
class AttendanceForm(forms.ModelForm):
class Meta:
model = Attendance
fields = '__all__'
These are models
class Employee(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
eid = models.IntegerField(primary_key=True)
salary = models.IntegerField()
gender = models.CharField(max_length=6, choices=GENDER_CHOICES, default=1)
contactno = models.CharField(max_length=10)
email = models.CharField(max_length=30)
country = models.CharField(max_length=30)
city = models.CharField(max_length=20)
pincode = models.IntegerField()
address = models.CharField(max_length=60)
def __str__(self):
return self.user.first_name + ' ' + self.user.last_name
class Attendance(models.Model):
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, default=1)
attendancedate = models.DateField()
in_time = models.TimeField()
out_time = models.TimeField()
description = models.TextField()
def __str__(self):
return str(self.employee)
view for attendance.
#csrf_exempt
def addattendance(request):
form = AttendanceForm()
emp_list = Employee.objects.all()
if request.method == 'POST':
form = AttendanceForm(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('employee/detail_attendance')
return render(request, 'employee/addattendance.html', {'form': form, 'emp_list': emp_list})
I tried everything, but I don't know why the data is not saving into the database. Also, models are created fine, and the main thing is that there are no errors coming up.
Please let me know if any changes are required.
I can suggest simple solution with Class-Based-Views:
from django.views.generic.edit import FormView
def AddAttendanceFormView(FormView):
form_class = AttendanceForm
extra_context = {"emp_list": Employee.objects.all()}
success_url = reverse_lazy('employee/detail_attendance')
template_name = 'employee/addattendance.html'
def post(self, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
return self.form_invalid(form)
Remember, that in urls.py you need to use .as_view() for class based views, like:
path((...), AddAttendanceFormView.as_view())
Also, you will not need #csrf_exempt, just put {% csrf_token %} anywhere inside your form's template.
Am actually new to programming, so i am just getting a blank page, no errors.
This is my model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile',
blank=True, null=True)
bio = models.TextField(max_length=500, null=True)
location = models.CharField(max_length=30, null=True)
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse('outcome:userprofile-detail', kwargs={'pk': self.pk})
class Post(models.Model):
text = models.TextField(max_length=255)
profile = models.ForeignKey('Profile', null=True, on_delete = models.CASCADE, related_name='create')
overview = models.CharField(max_length=255)
def __str__(self):
return self.text
The view
class Userlist(LoginRequiredMixin, ListView):
model = Profile
template_name = 'outcome/user-list.html'
class UserDetail(LoginRequiredMixin, DetailView):
model = Profile
template_name = 'outcome/userprofile_detail.html'
The template
{% for i in post.profile_set.all %}
**`trying to loop over the post`**
{{i.text}}
{% endfor %}
I have tried this for a while now and i dont know whether its from template or view.
You can override get_context_data method like this :
class UserDetail(LoginRequiredMixin, DetailView):
model = Profile
template_name = 'outcome/userprofile_detail.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of the user posts
all_posts = Post.objects.all()
user_posts = all_posts.filter(profile_id=self.request.user.profile.id)
context['user_posts'] = user_posts
return context
user_detail.html
{% for post in user_posts %}
{{post.text}}
{{post.overview}}
{% endfor %}
I got a question. I'm wondering what's the easiest way to add different {{ variable }} from different related model in one template.
I'd like to add some information related to seller rating:
def total_seller_ratings(self):
return self.seller.rating_seller.count()
def avg_seller_ratings(self):
return self.seller.annotate(avg_ratesugar=Avg('rating_seller__ratesugar')).order_by('-avg_ratesugar')
Create a template tag from Middleware? Or is any fastes solution to add another model on my view?
I got a main view for my main page:
#Channel MAIN PAGE
#method_decorator(login_required(login_url='/cooker/login'),name="dispatch")
class ChannelMainPage(generic.DetailView, FormMixin):
model = Channel
context_object_name = 'channel'
template_name = 'channel_detail.html'
form_class = ChannelChatForm
def get_context_data(self, **kwargs):
context = super(ChannelMainPage, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def form_valid(self, form):
if form.is_valid():
form.instance.channel = self.object
form.instance.user = self.request.user
form.save()
return super(ChannelMainPage, self).form_valid(form)
else:
return super(ChannelMainPage, self).form_invalid(form)
def post(self,request,*args,**kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_valid(form)
def get_success_url(self):
return reverse('channel:channel_detail',kwargs={"slug":self.object.slug})
models.py
class Channel(models.Model):
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_consumer", blank=True, null=True)
name = models.CharField(max_length=10)
seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_seller")
image = models.ImageField(null=True,blank=True,default='user/user-128.png', upload_to='channel/')
date_created = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField('Make it happen', default=False)
class Rating(models.Model):
channel = models.OneToOneField(Channel,on_delete=models.CASCADE,related_name="rating_channel")
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE,related_name='rating_consumer')
seller = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE,related_name='rating_seller')
is_rated = models.BooleanField('Already rated', default=True)
comment = models.TextField(max_length=200)
publishing_date = models.DateTimeField(auto_now_add=True)
ratesugar = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
def __str__(self):
return self.channel.name
One place to add it is on the Model class. This is already available as a variable within the context (see generic.Detailview.get_context_data). It is named "object" and can then be accessed through the templating mechanics.
class Channel(models.Model):
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_consumer", blank=True, null=True)
name = models.CharField(max_length=10)
seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_seller")
image = models.ImageField(null=True,blank=True,default='user/user-128.png', upload_to='channel/')
date_created = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField('Make it happen', default=False)
def some_method():
pass
template.html
<div>{{ object.some_method() }}</div>
Finally, on the discussion (code design) of which model to place the desired methods on...
If you want to calculate the average over ALL ratings it belongs on the User model.
If you want an average over the ratings over a given channel then it belongs on the Channel model.
Good luck.
PS. Don't forget to prefetch_related to have avoid slow queries!
I need some help with Django 2 and Python 3.
I'm using a CreateView to add new reccords in my database, but I need to make a filter for my Aviso form page to make the select field (field turma) to show only instances where the representante is the current user.
This is my model:
class Turma(models.Model):
nome = models.CharField(max_length=120, blank=False, null=False, help_text='Obrigatório.')
alunos = models.ManyToManyField(User, help_text='Obrigatório', related_name='alunos_matriculados')
data_cadastro = models.DateField(auto_now_add=True)
representante = models.ForeignKey(User, on_delete=models.PROTECT, blank=False, null=False)
colegio = models.ForeignKey(Colegio, on_delete=models.PROTECT, blank=False, null=False, help_text='Obrigatório.')
class Aviso(models.Model):
data_final = models.DateField(auto_now=False, auto_now_add=False, blank=False, null=False, verbose_name="Data Final")
comentarios = models.TextField(null=True, blank=True)
ultima_modificacao = models.DateField(auto_now=True)
data_post = models.DateField(auto_now_add=True)
turma = models.ForeignKey(Turma, on_delete=models.PROTECT, null=False, blank=False)
materia = models.ForeignKey(Materia, on_delete=models.PROTECT, null=False, blank=False)
This is my view:
class AvisoCreateView(LoginRequiredMixin, CreateView): #Cadastro de Aviso
template_name = 'form.html'
model = models.Aviso
login_url = '/login/'
success_url = reverse_lazy('visualizar_aviso')
fields = [
'turma',
'materia',
'tipo_aviso',
'comentarios',
'data_final'
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['titulo'] = 'Cadastrar aviso'
context['input'] = 'Adicionar'
return context
How could that be done?
You can add a queryset to the ForeignKey field.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'].fields['turma'].queryset = Turma.objects.filter(representante=self.request.user)
context['titulo'] = 'Cadastrar aviso'
context['input'] = 'Adicionar'
return context
You could create a ModelForm for that model.
And based on this answer you could override the forms __init__() method to alter the fields queryset.
class AvisoForm(forms.ModelForm):
class Meta:
model = Aviso
fields = [
'data_final', 'comentarios', 'ultima_modificacao', 'data_post',
'turma', 'materia',
]
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
# restrict the queryset of 'Turma'
self.fields['turma'].queryset = self.fields['turma'].queryset.filter(
representante=user)
Then, in your view, replace the attribute fields with form_class:
class AvisoCreateView(LoginRequiredMixin, CreateView):
...
form_class = AvisoForm
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Maybe you need to adjust a few things for your specific case.
Let us know if that solved it.
I am having an issue with an url and regular expression I get the error
AttributeError: Generic detail view EmployeeDetailView must be called with either an object pk or a slug.
What I am to achieve is to get to a user detail page coming from a specific project
url(r'^project/(?P<pk>[0-9]+)/$',views.ProjectDetailView.as_view(), name='ProjectDetails'),
url(r'^project/(?P<pk1>[0-9]+)/(?P<pk2>[0-9]+)/$',views.EmployeeDetailView.as_view(), name='EmployeDetails'),
my view is :
Project detail :
class ProjectDetailView(generic.DetailView, LoginRequiredMixin):
#import pdb; pdb.set_trace()
model = Project
template_name = 'project_details.html'
def get_context_data(self, **kwargs):
context = super(ProjectDetailView, self).get_context_data(**kwargs)
try:
team_name = Project.objects.get(id=self.kwargs['pk']).team_id.members.all()
context['team_name'] = team_name
except AttributeError:
pass
return context
class EmployeeDetailView(generic.DetailView, LoginRequiredMixin):
#import pdb; pdb.set_trace()
model = MyUser
template_name = 'Employee_Details.html'
def get_context_data(self, **kwargs):
context = super(EmployeeDetailView, self).get_context_data(**kwargs)
employee_name = MyUser.objects.get(id=self.kwargs['pk'])
context['employee_name'] = employee_name
return context
HTML link :
<span class="fa fa-id-card-o" aria-hidden="true"> Show Results
models:
MyUser models:
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
first_name = models.CharField(max_length=150, blank=True, null=True)
last_name = models.CharField(max_length=150, blank=True, null=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_hr = models.BooleanField(default=False)
is_candidate = models.BooleanField(default=False)
is_employee = models.BooleanField(default=False)
company = models.CharField(max_length=100, blank=True, null=True)
Project model:
class Project(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey('registration.MyUser', blank=True, null=True)
candidat_answers = models.ManyToManyField('survey.response')
Team models:
class Team(models.Model):
team_name = models.CharField(max_length=100, default = '')
team_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
members = models.ManyToManyField(MyUser, related_name="members")
could you please help me to figure it ? thx you ;)
Django doesn't know how to use pk1 and pk2 to fetch the object for the view. I would override the get_object method and fetch the object there.
from django.shortcuts import get_object_or_404
# Note mixin should come first
class EmployeeDetailView(LoginRequiredMixin, generic.DetailView):
model = MyUser
template_name = 'Employee_Details.html'
def get_object(self, queryset=None):
return get_object_or_404(MyUser, pk=self.kwargs['pk2'], project=self.kwargs['pk1'])
...
Another option is to set pk_url_kwarg = 'pk2'. This tells Django that pk2 is the primary key of the MyUser object, so there is no need to override get_object. However if you do this, then Django will ignore the pk1 from the URL.
class EmployeeDetailView(generic.DetailView, LoginRequiredMixin):
#import pdb; pdb.set_trace()
model = MyUser
pk_url_kwarg = 'pk2'
template_name = 'Employee_Details.html'