I am trying to implement a model that automatically filters data based on the user. I was able to modify the user model and add a value for "company" that references a table of companies.
I have a table that has a company value as well (call it data_table). Ideally when ANY user logs into the system, the data filtered to match company value on user to company value on data_table.
So far I have it working, but I want to overwrite the get_query method to incorporate what I have and I can't figure out how to get the current logged in user into the model without passing it from the view.
I would think that in the below I could just use in models.py:
current_user = request.user
cid = current_user.company.id
The advantage being I don't have to remember to put the filter in when I use the model.
The error that gets thrown is:
Exception Value:
global name 'request' is not defined
Here is what works:
models.py:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractUser
class Company(models.Model):
name = models.CharField(max_length=40, blank=True)
def __str__(self):
return self.name
class User(AbstractUser):
company = models.ForeignKey(Company, on_delete=models.CASCADE, default=1)
class data_tableManager(models.Manager):
def get_queryset(self, cid):
return super(data_tableManager, self).get_queryset().filter(company_filter_id=cid)
class data_table(models.Model):
company_filter = models.ForeignKey(Company, on_delete=models.CASCADE, default=1)
data_column = models.CharField(db_column='dc', max_length=10, blank=True, null=True)
objects = models.Manager() # would like to replace this with data_tableManager
company_objects = data_tableManager() # The company specific manager.
def __str__(self):
return self.firstname
views.py
from .models import data_table
#login_required(login_url='/login/')
def data_view(request):
current_user = request.user
cid = current_user.company.id
print current_user.get_company()
return render(
request,
'example.html',
{
'filtered1': data_table.objects.filter(company_filter_id=cid),
'filtered2': data_table.company_objects.get_queryset(cid),
'cid':cid,
}
)
Related
I have been spinning my wheels on this issue for a day or two. I have django web application that has 3 models
users/models.py:
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
# Create your models here.
class ExtendedUser(models.Model):
user=models.OneToOneField(User,null=True, on_delete=models.CASCADE)
def full_name(self):
return (self.user.get_full_name())
def __str__(self):
return self.user.get_full_name()
#receiver(post_save, sender=User)
def create_or_update_user_extendeduser(sender, instance, created, **kwargs):
if created:
ExtendedUser.objects.create(user=instance)
instance.extendeduser.save()
playground/models.py:
class Customer(models.Model):
def __str__(self):
return self.Customer_Name
Customer_Name = models.CharField(max_length=100)
SFDC_Customer_Record_Number = models.IntegerField(default='')
Zone = models.CharField(max_length=50, default='')
Government = models.BooleanField(default=False)
date_posted = models.DateTimeField(default=timezone.now)
customerauthor = models.ForeignKey(ExtendedUser, on_delete=models.DO_NOTHING,default=ExtendedUser)
def get_absolute_url(self):
return reverse('playground-home')
class Vue_Quote(models.Model):
def __str__(self):
return self.Quote_Name
Quote_Name = models.CharField(max_length=100)
SFDC_Golden_Opp_ID = models.IntegerField()
Vue_System_Count = models.IntegerField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(ExtendedUser, on_delete=models.DO_NOTHING,default=ExtendedUser,blank=True,null=True)
Quote_Type = models.CharField(max_length=100)
Customer = models.ForeignKey(Customer, on_delete=models.DO_NOTHING, default='')
def get_absolute_url(self):
return reverse('quote-detail',kwargs={'pk': self.pk})
I am using the 3rd party application django-extra-views to create a single form which allows a user to create a customer and quote at the same time. Views.py:
class QuoteInline(InlineFormSetFactory):
model = Vue_Quote
fields = ['Quote_Name','SFDC_Golden_Opp_ID','Vue_System_Count','Quote_Type',]
factory_kwargs = {'extra':1}
class CreateQuoteInlinesView(CreateWithInlinesView):
model = Customer
inlines = [QuoteInline]
fields = ['Customer_Name','SFDC_Customer_Record_Number','Zone','Government']
template_name= 'quote_and_customer.html'
def forms_valid(self, form, inlines):
form.instance.customerauthor = ExtendedUser.objects.get(user=self.request.user)
return super().forms_valid(form,inlines)
All of this is working great except for that I am not able to save the author for the Vue_Quote model...I always get "None":
Image of Vue_Quote.author = None from my form
I have tried a wide range of solutions but cannot seem to solve this and I am finding very little documentation on django-extra-views to support my finding a solution.
Any assistance is greatly appreciated!
Welp, I've looked at the source code and tried to figure it out.. It's really abstract and a lot of inheritance
This would be my best guess:
def forms_valid(self, form, inlines):
user = ExtendedUser.objects.get(user=self.request.user)
form.instance.customerauthor = user
for i in inlines:
i.instance.author = user
i.save()
return super().forms_valid(form,inlines)
Or if you wanted to also define the Customer field in this forms_valid you could do it after the super call
You could try something like this:
def forms_valid(self, form, inlines):
user = ExtendedUser.objects.get(user=self.request.user)
form.instance.customerauthor = user
customerObj = super().forms_valid(form,inlines)
for i in inlines:
i.instance.author = user
i.instance.Customer = customerObj
i.save()
return customerObj
I'm trying to pass two models into a create view, where i am trying to get the the primary key from the URL to retrieve the details from a food truck model so it can be displayed in the page, and where a user can write a review about food truck. Also, I'd like a list of the reviews to be displayed on the page.
views.py
class TruckReviewView(CreateView):
model = Review
template_name = 'truckReviews/detail.html'
fields = ['speedOfService', 'qualityAndTaste', 'valueForMoney', 'comment']
def get_queryset(self):
self.pk = self.kwargs['pk']
queryset = super(TruckReviewView, self).get_queryset()
return queryset
def get_context_data(self, **kwargs):
context = super(TruckReviewView, self).get_context_data(**kwargs)
context['truck'] = FoodTrucks.objects.get(truckID=get_queryset())
context['reviews'] = Review.objects.get(truckID=get_queryset())
return context
urls.py
urlpatterns = [
path('', TruckListView.as_view(), name='reviews-home'),
path('truck/<int:pk>/', TruckReviewView.as_view(), name='truck-detail'),
path('about/', About.as_view(), name='reviews-about'),
]
models.py
class FoodTrucks(models.Model):
truckID = models.IntegerField(primary_key=True, unique=True, null=False)
name = models.CharField(max_length=25)
category = models.CharField(max_length=20)
bio = models.TextField()
avatarSRC = models.TextField(default=None)
avatarALT = models.CharField(max_length=20, default=None)
avatarTitle = models.CharField(max_length=20, default=None)
coverPhotoSRC = models.TextField(default=None)
coverPhotoALT = models.CharField(max_length=20, default=None)
coverPhotoTitle = models.CharField(max_length=20, default=None)
website = models.TextField(default=None)
facebook = models.CharField(max_length=100, default=None)
instagram = models.CharField(max_length=30, default=None)
twitter = models.CharField(max_length=15, default=None)
class Review(models.Model):
reviewID = models.AutoField(primary_key=True, unique=True, serialize=False, null=False)
truckID = models.ForeignKey(FoodTrucks, on_delete=models.CASCADE)
userID = models.ForeignKey(User, on_delete=models.CASCADE)
datePosted = models.DateTimeField(default=timezone.now)
speedOfService = models.IntegerField()
qualityAndTaste = models.IntegerField()
valueForMoney = models.IntegerField()
comment = models.TextField(max_length=128)
I've tried to use get_queryset to get the pk from the URL and pass the pk into get_context_data and target the specific truck with that ID in the database.
The difficulty comes from the fact that you combine a list view and create view. If you want to combine this into one view, then you need to do a bit mixing and matching with different mixins of the Class Based Views.
It can be done, but it's not trivial. If you're new to Django, then this may be overshooting things. I've renamed fields an such and did it as an exercise. I haven't bothered with the form submission, it shouldn't be that difficult to implement as the other views don't deal with the methods involved (form_valid, get_success_url, etc). You can use it as a guide to see what you should be learning. The above linked site is extremely convenient to see how things are mixed together.
The result below will provide the variables "foodtruck", "reviews" and "form" to the template.
import typing as t
from django.views import generic
from .models import FoodTruck, Review
from .forms import ReviewForm
if t.TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse
from django.contrib.auth.models import AbstractUser
class AuthenticatedRequest(HttpRequest):
user: AbstractUser = ...
class FoodTruckDetailReviewListCreateView(
generic.list.MultipleObjectMixin, generic.edit.CreateView,
):
template_name = "foodtrucks/detail.html"
model = Review
list_model = Review
context_list_name = "reviews"
context_object_name = "foodtruck"
detail_model = FoodTruck
form_class = ReviewForm
def get(self, request: "AuthenticatedRequest", *args, **kwargs) -> "HttpResponse":
"""
Combine the work of BaseListView and BaseDetailView
Combines the get implementation of BaseListView and BaseDetailView, but
without the response rendering. Then hands over control to CreateView's
method to do the final rendering.
Some functionality is stripped, because we don't need it.
:param request: The incoming request
:return: A response, which can be a redirect
"""
# BaseListView
self.object_list = self.get_queryset()
# BaseDetailView
self.object = self.get_object()
context = self.get_context_data(
object=self.object, object_list=self.object_list
)
# CreateView sets self.object to None, but we override form_kwargs, so
# we can leave it at a value.
return self.render_to_response(context=context)
def get_template_names(self):
# Bypass logic in superclasses that we don't need
return [self.template_name]
def get_object(self, queryset=None):
# We provide the queryset to superclasses with the other model
return super().get_object(queryset=self.detail_model.objects.all())
def get_queryset(self):
# This only gets called by MultipleObjectMixin
pk = self.kwargs.get(self.pk_url_kwarg)
if pk is None:
raise AttributeError(
"Unable to filter on food truck: {} is missing in url.".format(
self.pk_url_kwarg
)
)
queryset = self.list_model.objects.filter(food_truck_id=pk)
# print(str(queryset.query))
return queryset
def get_context_data(self, **kwargs):
if "object" in kwargs:
kwargs[self.context_object_name] = kwargs["object"]
if "object_list" in kwargs:
kwargs[self.context_list_name] = kwargs["object_list"]
return super().get_context_data(**kwargs)
def get_form_kwargs(self):
# Bypass ModelFormMixin, which passes in self.object as instance if it
# is set.
return super(generic.edit.ModelFormMixin, self).get_form_kwargs()
And as a reference, this is what I changed the models to:
import uuid
from django.contrib.auth import get_user_model
from django.db import models
from django.utils import timezone
class FoodTruck(models.Model):
name = models.CharField(max_length=25)
category = models.CharField(max_length=20)
bio = models.TextField()
avatar_url = models.URLField(blank=True)
avatar_alt_text = models.CharField(max_length=20, blank=True)
avatar_title = models.CharField(max_length=20, blank=True)
cover_photo_url = models.URLField(blank=True)
cover_photo_alt_text = models.CharField(max_length=20, default="No photo provided")
cover_photo_title = models.CharField(max_length=20, default="No photo provided")
website = models.URLField(blank=True)
facebook = models.CharField(max_length=100, blank=True)
instagram = models.CharField(max_length=30, blank=True)
# https://9to5mac.com/2017/11/10/twitter-display-name-limit/
twitter = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.name
class Review(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
food_truck = models.ForeignKey(
FoodTruck, on_delete=models.CASCADE, related_name="reviews"
)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
posted_at = models.DateTimeField(default=timezone.now)
speed_of_service = models.IntegerField()
quality_and_taste = models.IntegerField()
value_for_money = models.IntegerField()
comment = models.TextField(max_length=128)
def __str__(self):
return "Review about {} by {}".format(
self.food_truck.name, self.user.get_full_name()
)
And finally the form (with a bit of trickery to inject bootstrap classes):
class ReviewForm(forms.ModelForm):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for field in self.fields.values():
if not field.widget.is_hidden:
field.widget.attrs.setdefault("class", "form-control")
class Meta:
model = Review
exclude = ("uuid", "user", "food_truck", "posted_at")
First, there's not need to create a truckID and reviewID primary key fields because Django creates a unique id field for each object automatically on which you can simply do .get(id=1) or .filter(id=1) etc.
Just like it is completely useless to put ID in fields with Foreign Key or any relational fields because Django will automatically take the name and append _id to it. For instance, just user would become user_id or truck would be truck_id in backend on which you can do .get(user__id=1) or .get(user_id=1) for example.
You should review this section of your code. You're actually not doing anything with the primary key:
def get_queryset(self):
queryset = super().get_queryset()
try:
item = queryset.get(id=self.kwargs['pk'])
except:
...
else:
# Do something with item here
...
finally:
return queryset
or, with get_context_data:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
queryset = super().get_queryset()
try:
item = queryset.get(id=kwargs['pk'])
except:
...
else:
# Do something with item here
context['item'] = item
finally:
return context
I'm sorry but your question is a bit confusing. If you're trying to get details from a model you should use a DetailView. Also, on a DetailView assuming you want the details of a Review since you have the truck on a review you could simply override the get_context_data and set truck in the context by doing self.object.truck.
If you're trying to create a review then it's right to use the CreateView but that should only be for the Review model.
To list you should use a ListView.
So, to my understanding, you have a truckID and want to create a review for that. In that case, it'd have a CreateView for Review model.
Have a look at the CreateView, DetailView and ListView docs
I am trying to create a ModelForm for my Model Class "Asset" in Django 3
from django.db import models
from django.contrib.auth.models import User
from django.forms import ModelForm
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Asset(models.Model):
serial = models.CharField(max_length=200)
manufacturer = models.ManyToManyField(Manufacturer)
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200)
I managed to create a Form via the following code
from django import forms
from .models import Manufacturer
class AssetForm(forms.Form):
serial = forms.CharField(max_length=150)
manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all().values('name'))
name = forms.CharField(max_length=200)
description = forms.TextInput()
status = forms.CharField(max_length=200)
category = forms.CharField(max_length=200)
The querySet results in a dropdown being filled out with either "{'name':'Apple'}" or "('Apple',)" depending on using values or values_list respectively. How can I just display the name itself?
Adding the following method to the model fixes the problem:
def __str__(self):
return self.name
This will return the name and only the name to the queryset.
I'm learning in DJango and I have learned alot of stuff from the documentation and also in StackOverflow. Right now, I'm kinda stuck and I just want to know who can I check in a class based view, if the user is in the manager column in job model/ It can also be in the manager model that's fine too.
I tried using UserPassesTestMixinin order to check if user is part of it but I'm getting an error of Generic detail view createjob must be called with either an object pk or a slug in the URLconf.
I just need someone to point me to the right direction or give me a hint.I also tried, this:
class createjob (LoginRequiredMixin,CreateView):
model = Job
fields = ['member','title', 'description', 'file']
def form_valid(self,form):
form.instance.manager=self.request.user
return super().form_valid(form)
But it's giving me an error of Cannot assign "<SimpleLazyObject: <User: edlabra>>": "Job.manager" must be a "Manager" instance.
Here's my views.py:
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import ListView, CreateView
from .models import Job, Member
from profiles.models import User
from django.contrib.auth.decorators import login_required
# Create your views here.
class jobs(LoginRequiredMixin,ListView):
model = Job
template_name = 'users/user_jobs.html'
context_object_name = 'jobs'
def get_queryset(self):
return Job.objects.filter(member__member=self.request.user)
class createdjobs(LoginRequiredMixin,ListView):
model = Job
template_name = 'users/manager_jobs.html'
context_object_name = 'jobs'
def get_queryset(self):
return Job.objects.filter(manager__manager=self.request.user)
class teamview(LoginRequiredMixin,ListView):
model = Member
template_name = 'users/manage_team.html'
context_object_name = 'members'
def get_queryset(self):
return Member.objects.filter(manager__manager=self.request.user)
class createjob (LoginRequiredMixin,UserPassesTestMixin,CreateView):
model = Job
fields = ['member','title', 'description', 'file']
def test_func(self):
job=self.get_object()
if self.request.user == Job.manager:
return True
return False
Models.py:
from django.db import models
from profiles.models import User
# Create your models here.
class Points (models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
points = models.IntegerField(default=0, null=False)
def __str__(self):
return self.user.username
class Profile (models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png',upload_to='profile_pics')
def __str__(self):
return f'{self.user.username}Profile'
class Manager (models.Model):
name = models.CharField(max_length=30, blank=True, null=True)
manager = models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Member (models.Model):
name = models.CharField(max_length=30, blank=True, null=True)
manager = models.ForeignKey(Manager, on_delete=models.CASCADE)
member = models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Job (models.Model):
manager = models.ForeignKey(Manager, on_delete=models.CASCADE)
member = models.ForeignKey(Member, on_delete=models.CASCADE)
title = models.CharField(max_length=30, blank=False, null=False)
description = models.TextField()
datePosted = models.DateTimeField (auto_now = True)
file = models.FileField(null=True, blank=True,upload_to='job_files')
def __str__(self):
return self.title
assign user from manager table.
def form_valid(self,form):
form.instance.manager=Manager.objects.get(manager=self.request.user)
return super().form_valid(form)
I have users who listed their textbook.
I need to count objects in Textbook model and display total count in the side menu.
Here is my Model
from django.db import models
from django.http import HttpResponse
from django.urls import reverse
from django.contrib.auth.models import User
from django.utils.functional import cached_property
class Textbooks(models.Model):
owner = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True)
title = models.CharField(max_length=1000)
isbn = models.CharField(max_length=20)
author = models.CharField(max_length=250)
edition = models.CharField(max_length=50)
rrp = models.CharField(max_length=30)
about = models.TextField(max_length=1000, null=True)
textbook_image = models.FileField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('textbooks:detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
I used Custom template tag
class CustomTag(template.Node):
def render(self, context):
context['my_custom_tag_context'] = Textbooks.objects.filter(owner=self.user.request).count()
return ''
#register.tag(name='get_custom_tag')
def get_custom_tag(parser, token):
return CustomTag()
enter image description here
AttributeError at /
'CustomTag' object has no attribute 'user'. It seems that i cant use filter in template tag.
is there any other way i can filter them and show the count by owner who is logged in?
Here is what i intend to have.
enter image description here
You have to change below line in...
user = context['request'].user
context['my_custom_tag_context'] = Textbooks.objects.filter(owner=user).count()
instead of
context['my_custom_tag_context'] = Textbooks.objects.filter(owner=self.user.request).count()
You can get user from request.