Django admin site for limited privilege users - django

I have a Django application, and have two apps within the project: accounts and store. In my store app, I have Product, Category model and other models.
I have admin site, from where I can register Products. In the accounts, I allow general people to signup and log in.
Now, I also want users to be able to register Products, so that they can sell their own products. However, I do not want to give them to a complete access to the admin site.
How can I give such limited access to the admin site to general users?
Thanks.

In admin you can make groups and assign access to models you wanted and same could be applied to users but you might be interested in limited access to models records to which logged user have itself added. in order to achieve this you have to define one column in model to foreign key to users like below I have defined model for company and assigned each company to user:
class Company(models.Model):
name = models.CharField(max_length=64, primary_key=True)
kam = models.ForeignKey(User, verbose_name='KAM', blank=True, null=True)
address = models.TextField(blank=True, null=True)
city = models.CharField(max_length=32, blank=True, null=True)
country = models.CharField(max_length=32, blank=True, null=True)
phone = models.CharField(max_length=32, blank=True, null=True)
fax = models.CharField(max_length=32, blank=True, null=True)
url = models.URLField(blank=True, null=True)
class Meta:
verbose_name_plural = 'Companies'
#unique_together = (('name', 'kam'),).
def __unicode__(self):
return self.name
Now your model will be associated with user, Now you could restrict records to be loaded according to logged user using admin.py in modeladmin definition like given below:
def queryset(self, request):
qs = super(CompanyAdmin, self).queryset(request)
# If super-user, show all comments
if request.user.is_superuser:
return qs
return qs.filter(kam=request.user)
thats simple let me know if this is what you want?
Also you could assign read only right. in model admin

Admin uses a class ModelAdmin to render the page as you would probably already know. That class has a queryset method which you override based with a new filter, based on who is accessing the site, as suggested by sharafjaffri.
But that filtering alone isn't sufficient. You also need to filter the values displayed in the dropdowns, to only those created by the user. And then when saved, you should associate the new object with the portal of the user adding it.
Here is my quick untested implementation of the same:
class PortalAdmin(admin.ModelAdmin):
exclude = ('portal',)
def queryset(self, request):
"""
Filter the objects displayed in the change_list to only
display those for the currently signed in user.
"""
qs = super(UserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(portal=request.user.profile.portal)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
the_model = db_field.related.parent_model
if hasattr(the_model,'portal'):
kwargs['queryset'] = the_model.objects.filter(portal=request.portal)
return super(PortalAdmin,self).formfield_for_foreignkey(db_field, request, **kwargs)
def save_model(self, request, obj, form, change):
if not change:
obj.portal = request.portal
obj.save()

Related

how to allow users to rate a product only once,

a Reviews model linked to Users and Product, nothing special,
users can vote as much as they want which is working,
but i'm stuck restricting the rating to just once
the view so far:
class ReviewCreateView(CreateView):
model = Review
form_class = ReviewForm
template_name = "form.html"
success_url = reverse_lazy('product_list')
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
model
class Review(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT, null=True,related_name='reviews')
user = models.ForeignKey(User, on_delete=models.PROTECT, null=True,related_name='reviewers')
rating = models.IntegerField(null=True, blank=True, default=0)
comment = models.TextField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
the other models wouldn't help that muc ,Product just describe a product ,and default (regular) User model
the form is ModelForm based on the Review Model itself
any suggestion is welcome, even if i have to redo the model, thank you very much
set db constraint like so:
class Meta:
constraints = [
UniqueConstraint([fields=['user', 'product'], name='product_user_unique'),
]
the condition for your views would be like this:
def get(self, request, product):
if (Review.objects.filter(user=request.user)
.filter(product_id=product).exists():
# probably, you'd want to pass some
# conditional data to the template's context here
do_something()
That could be it. You restrict non-unique reviews by UI, but if it's somehow bypassed, user will get a server error. Of course, you could also apply validation in your CreateView (or analogous).
From architectural standpoint, you can always create a review_user table which will have the Product ID, and the ID of the users who voted on that product.. and upon second vote, you check if there's already a vote in the table.
Once you have this understanding, I am sure that you will find a way to incorporate the logic within your code.

Django admin groups permissions and access

I'm searching for a way to customize the Django Administration to support permissions and data based on the user group.
For example, I've just created the Developers1, Developers2 groups.. now I've also created the Transaction model, with AdminModel to specify how to list data.
Transacton model:
class Transaction(models.Model):
income_period_choices = (('Weekly', 'Weekly'), ('Fortnightly',
'Fortnightly'))
chp_reference = models.CharField(max_length=50, unique=True)
rent_effective_date = models.DateField(null=True, blank=True)
income_period = models.CharField(max_length=11,
choices=income_period_choices,
null=True,
blank=True)
property_market_rent = models.DecimalField(help_text='Weekly',
max_digits=7,
decimal_places=2,
null=True,
blank=True)
*group = models.ForeignKey(Group, on_delete=models.CASCADE)
im not sure about the *group field, should i delete it , or should i create Charfield, which is not a foreignkey to the django.contrib.auth.group model?
and this is the admin transaction:
#admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)
def get_queryset(self, request):
qs = super().get_queryset(request)
# for s in qs:
if request.user.is_superuser:
return qs
return qs.filter(group_name__in=Group)
search_fields = ['chp_reference','familymember__name']
inlines = [FamilyGroupInline,FamilyMemberInline]
what im trying to do is i want each group to only access its own Transaction model, and each group can add, delete, update and view their own Transactions only(eg developers1 group cant access developers2 Transactions and vice versa)
any thoughts should be appreciated
thanks!:)
To do that you can override the get_queryset method to only return the objects that are relevant to these users (based on their permissions, groups or any condition you want).

django Filter customers in the specific user groups

I want to display Customers from specific groups in the ListView, not able to understand how to get the queryset
class CustomerList(ListView):
model = Customer
queryset = Customer.objects.filter(member__groups__name__in=['online', 'whatsapp'])
template_name = 'customer/customer_list.html'
models.py
class Customer(models.Model):
member = models.ForeignKey(User, verbose_name=_("Customer"), on_delete=models.CASCADE)
contact = models.ForeignKey(Contact, verbose_name=_("Contact"), on_delete=models.CASCADE, blank=True, null=True)
...
Customers are added to the groups as below:
class AddUser(CreateView):
def post(self, request, *args, **kwargs):
form = UserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
group, created = Group.objects.get_or_create(name='online')
user.groups.add(group)
user.save()
I have similar code working, check if this works for you -
class ProfessorsList(generic.list.ListView):
model = Staff
queryset = Staff.objects.filter(member__groups__name='teaching')
For multiple groups, you would do as: (I believe you're already doing it...)
Customer.objects.filter(member__groups__name__in=['online', ...])
If it still does not work for you try like this:
users = User.objects.filter(groups__name__in=[your_groups])
queryset = Customer.objects.filter(member__in=users)
Make sure customers have users and users are part of your_groups

Django Customize many to many query

I have extended Django's default User class by adding a ManyToManyField to another table called Algorithm. The new User class is:
class User(AbstractUser):
name = CharField(_("Name of User"), blank=True, max_length=255)
algorithms = ManyToManyField(Algorithm, blank=True, default=None, related_name="users")
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
I want to specify if the user is_superuser or is_staff then User.algorithms.all() will get all algorithms. Otherwise, get only the records in the pivot table User_user_models. How can this be achieved? I tried to add a property method that check if superuser/staff then return all otherwise return super but it didn't work.
P.S: during creating/editing the user, if user is set to superuser or staff, then there is no need to choose algorithms.
I would just overwrite the save() method:
class User(AbstractUser):
name = CharField(_("Name of User"), blank=True, max_length=255)
algorithms = ManyToManyField(Algorithm, blank=True, default=None, related_name="users")
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
def save(self, **kwargs):
if self.is_superuser or self.is_staff:
self.algorithms = Algorithm.objects.all()
else:
# Get your users from your pivot table
super().save()

Try to join a OneToOne relationship in Django

I need some help doing a join using Django, which seems like it should be easy. I have looked at the documentation but it seems like it won't join for some reason.
I am trying to get in my view, the model.Photo and model.PhotoExtended with both joined and then displayed in the view. Currently I am just trying to get the model.Photo displayed but with a join which finds the request.user and filters it based on that.
They are in different apps.
models.py for model.Photo
class Photo(ImageModel):
title = models.CharField(_('title'),
max_length=60,
unique=True)
slug = models.SlugField(_('slug'),
unique=True,
help_text=_('A "slug" is a unique URL-friendly title for an object.'))
models.py for model.PhotoExtended
class PhotoExtended(models.Model):
Photo = models.OneToOneField(Photo, related_name='extended', help_text='Photo required', null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, help_text='User that uploaded the photo')
views.py
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
return Photo.objects.filter(photoextended__user=user)
You set the related_name on Photo (which shouldn't be capitalized by the way) to extended so you need to filter like so:
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
# 'extended' vs. 'photoextended'
return Photo.objects.filter(extended__user=user)