I have a Users and Jobs. If some User creates a Job, then and only then he/she can edit some information of this Job.
So he visits the url .../job/update/<id>. If the Job is created by him (the User is a ForeignKey in Job, then he can modify data. Otherwise he gets 404 error.
In view function, I would probably get current Users id and compare this id to Jobs ForeignKey.
But there are many patterns and shortcuts in class views so I'm curious how to do that this way.
class EditOrderView(UpdateView):
model = Job
fields = ['language_from','language_to','level','short_description','notes',
'text_to_translate','file']
template_name = 'auth/jobs/update-order.html'
class Job(models.Model):
customer = models.ForeignKey(User, related_name='orders', help_text=u"Zákazník")
translator = models.ForeignKey(User, related_name='jobs', null=True, blank=True, help_text=u"Prekladateľ")
price = models.FloatField(null=True, blank=True, help_text=u"Cena")
language_from = models.ForeignKey(Language, related_name='jobs_from', null=True)
language_to = models.ForeignKey(Language, related_name='jobs_to', null=True)
...
It looks like you can override .get_object() method and include your own logic:
from django.shortcuts import get_object_or_404
class EditOrderView(UpdateView):
model = Job
...
def get_object(self, queryset=None):
return get_object_or_404(self.model, pk=self.kwargs["pk"], customer=self.request.user)
Related
The Viewset def list looks like this:
class ThreeDimensionalModelViewSet(viewsets.ViewSet):
serializer_class = ThreeDimensionalModelSerializer
queryset = ThreeDimensionalModel.objects.all()
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def list(self, request):
models = ThreeDimensionalModel.objects.all()
serializer = ThreeDimensionalModelSerializer(models, many=True)
print(request.user.id)
return Response(serializer.data)
The serializer looks like this:
class ThreeDimensionalModelSerializer(serializers.ModelSerializer):
class Meta:
model = ThreeDimensionalModel
fields = ['File', 'Uploaded', 'Owner', 'Previous', 'SharedWithUser']
read_only_fields = ['Owner']
The model looks like this:
class ThreeDimensionalModel(models.Model):
File = models.FileField(upload_to='models')
Owner = models.ForeignKey('auth.User', on_delete=models.SET_NULL, null=True, related_name='Owner')
Uploaded = models.DateTimeField(auto_now_add=True)
Previous = models.ForeignKey("self", on_delete=models.SET_NULL, default=None, null=True)
SharedWithUser = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='SharedWithUser')
When a user requests models at /api/models it should only show the models that are the same owner Id as his.
If no additional data is sent with that request then obviously you can't filter by user.
The straightforward way to do it is that for logged in users the cookie will contain user information such as userId.
When your endpoint recognizes the user who made the requested is logged in, it will use that as the filter for the query instead of all() as seen in the Django docs
https://docs.djangoproject.com/en/3.2/topics/db/queries/#retrieving-specific-objects-with-filters
To summarize - if the user is not logged in (or supplies the information as part of the request in some way) then the request is anonymous and there is no way to know who made it
I searched through stackoverflow about this particular scenario, but could not find a concrete answer, so i'm posting this.
So my problem is that i need to display specific records to a specific user in Django Admin. I'm aware that i can get the concrete logged in user through the get_queryset method extracting it from the request object. But the issue is i need to look through 6 tables to get to the information about the user of the recommendations so i could know which recommendation to display to him.
For example, if the records i need to display come from a Recommendation table, it has a reference to TableA, which has a reference to TableB .... which has a reference to TableF which has a reference to the User.
I'm aware i could do this by executing a plain SQL query with multiple joins, but my guess is that there must be a pythonic or Django sophisticated solution to this. But i may be wrong.
The model is unfortunately not in my control, nor i can change it, so i'm left to work with the state of the model that there is.
Thanks in advance.
EDIT: Unfortunately, i can't share details of it, but i can share the general look of it. So i think this should be enough to have a picture of my problem.
from django.db import models
from django.contrib.auth.models import User
class TableF(models.Model):
information = models.CharField(max_length=256, null=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class TableE(models.Model):
information = models.CharField(max_length=256, null=False)
tableF = models.ForeignKey(TableF, on_delete=models.CASCADE)
class TableC(models.Model):
information = models.CharField(max_length=256, null=False)
tableEs = models.ManyToManyField(TableE, through='TableD')
class TableD(models.Model):
information = models.CharField(max_length=256, null=False)
tableC = models.ForeignKey(TableC, on_delete=models.CASCADE)
tableE = models.ForeignKey(TableE, on_delete=models.CASCADE)
class TableA(models.Model):
information = models.CharField(max_length=256, null=False)
tableCs = models.ManyToManyField(TableC, through='TableB')
class TableB(models.Model):
information = models.CharField(max_length=256, null=False)
tableA = models.ForeignKey(TableA, on_delete=models.CASCADE)
tableC = models.ForeignKey(TableC, on_delete=models.CASCADE)
class Recommendation(models.Model):
information = models.CharField(max_length=256, null=False)
tableA = models.ForeignKey(TableA, on_delete=models.CASCADE)
you can use a middleware to include de user to the thread locals and catch this user from get_queryset in the model manager.
from threading import local
_thread_locals = local()
def get_current_user():
return getattr(_thread_locals, 'user', None)
class ThreadLocals(object):
#staticmethod
def process_request(request):
_thread_locals.user = getattr(request, 'user', None)
in the settings
MIDDLEWARE = [
...
'path.to.file.ThreadLocals',
]
from your.path import get_current_user
class TableFManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(creator=get_current_user())
class TableF(models.Model):
information = models.CharField(max_length=256, null=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
objects = TableFManager()
another less invasive option could be to rewrite the get_queryset in the admin class. there you already have the user in the request
def get_rec_user(user):
tes = TableE.objects.filter(tableF__in=TableF.objects.filter(user=user))
aes = TableB.objects.filter(tableE__in=tes).values_list('tableA_id', flat=True)
return Recommendation.objects.filter(
tableA__in=TableA.objects.filter(id__in=aes)
)
I'm using Django multi-table inheritance to implement a notifications system.
It looks like this:
class Notification(models.Model):
# this allows us to check the type without having to query another table
type = models.CharField(max_length=2, choices=type_choices)
user = models.ForeignKey(User, related_name='+', null=True)
date = models.DateTimeField(default=datetime.now)
read = models.BooleanField(default=False)
class Meta:
ordering = ["-date"]
# Users can comment on items.
class CommentNotification(Notification):
comment = models.ForeignKey(Comment, related_name='+')
class ShareNotification(Notification):
share = models.ForeignKey(Share, related_name='+')
# If user unsubscribes from an item, they will not receive notifications of comments on that item
class UnsubscribeItem(models.Model):
user = models.ForeignKey(User, related_name='+')
item = models.ForeignKey(Item, related_name='+')
class Comment(models.Model):
item = models.ForeignKey(Item, related_name='comments')
user = models.ForeignKey(User, related_name='+')
comment = models.TextField()
If I want to get all notifications for a user, I can simply query the Notification table. But I also want to exclude any CommentNotification entries if the user has unsubscribed from that item (only if there is an UnsubscribeItem with user=request.user and item=comment.item).
The problem of course is the field I want to filter is not on the base class. Is it possible to modify the queryset itself to exclude those entries? Or do I need to exclude them while serializing the collection? (I'm using django-rest-framework to serialize for my API, if that helps.)
I have this model in Django, where a person has the same information from the user provided by Django plus a little bit more information. When I create a new person it requires to create a new user also, that's fine. But when I delete a person the user still remains on my database. What am I missing here ? I would like to delete the user too.
class Person(models.Model):
user = OneToOneField(User)
gender = CharField(max_length=1, choices=GenderChoices, blank=True, null=True)
birth_date = DateField(blank=True, null=True)
def __unicode__(self):
return self.user.username
Try to override the delete method on the model (code not tested):
class Person(models.Model):
user = OneToOneField(User)
gender = CharField(max_length=1, choices=GenderChoices, blank=True, null=True)
birth_date = DateField(blank=True, null=True)
def __unicode__(self):
return self.user.username
def delete():
theuser = User.objects.get(id=user)
theuser.delete()
I have found some relevant documentation about CASCADE usage in Django here.
I have models.py like this
class Faculty(TimeStampedModel):
_registry = []
faculty_code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=255)
date_of_birth = models.DateField(default=datetime.date.today)
age = models.IntegerField()
current_subjects = models.CharField(max_length=255)
research_interest = models.CharField(max_length=255, blank=True)
joining_year = models.CharField(
max_length=5, )
projects = models.CharField(max_length=255, blank=True)
now the user can't add Faculty but can view those created from the admin. In order to do so the user must enter the faculty_code in a form after which I have to get the other fields and show it to the user.
I don't how to implement this. I mean anytime we create a django-form and use POST method it creates a new object I don't want that. User should send a 'GET' request to fetch data of other fields of the class.
I don't think a form is necessary for this, use a DetailView.
url
urlpatterns = patterns('',
url(r'^faculty/(?P<slug>\w+)/$', FacultyDetail.as_view()),
)
view
from django.views.generic.detail import DetailView
class FacultyDetail(DetailView):
model = Faculty
slug_field = 'faculty_code'
More info:
docs
ccbv