How to select all users I don't follow in Django Models - django

I'm trying to build web app with Django on my backend and I want to make some type of people recommendations and show only people the current user doesn't follow, however I don't know how to do this.
It seems like there are questions on StackOverflow however they are about SQL queries and I still only know the Django Models for the database.
I use the standard Django User Model and the following model for the following relationship
class Follow(models.Model):
# This means following(Person A) follows follower(Person B)
following = models.ForeignKey(User, on_delete=models.CASCADE, related_name='following')
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name='follower')
def __str__(self):
return str(self.following.username) + " follows " + str(self.follower.username)
class Meta:
unique_together = (('following', 'follower'), )
I use the following query to get all the users the current user follows, but I would like to sort-of invert it.
Follow.objects.filter(following=user.id).prefetch_related('following')

Use exclude insteed of filter
Follow.objects.exclude(following=user.id).prefetch_related('following')

Related

Django: Validate relationships among ForeignKeys

I have a model Run with two ForeignKeys, signup and report,
class Run(models.Model):
signup = models.ForeignKey(Signup, on_delete=models.CASCADE, related_name="runs")
report = models.ForeignKey(Report, on_delete=models.CASCADE, related_name="runs")
kind = ...
pointing to models, which in turn are related to another model, Training,
class Report(models.Model):
training = models.OneToOneField(
Training, on_delete=models.CASCADE, primary_key=True
)
cash_at_start = ....
class Signup(models.Model):
training = models.ForeignKey(
Training, on_delete=models.CASCADE, related_name="signups"
)
participant = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="signups"
)
When creating a Run I would like to make sure, that it's signup and report are for the same Training, i.e. that report.training == signup.training.
What is this kind of validation called? And how would I achieve it?
Also, I am happy to learn other ways to implement this, if another database structure would be better.
Here are the docs that describe the validation process on models.
Please note that this validation usually only happens with ModelForms (when calling form.is_valid()). Manually creating an object and using save() doesn't trigger this validation if you don't call model_instance.full_clean() method. It's aimed at the user, not the developer.
According to the mentioned docs, my suggestion is to use the clean method:
class Run(models.Model):
signup = models.ForeignKey(Signup, on_delete=models.CASCADE, related_name="runs")
report = models.ForeignKey(Report, on_delete=models.CASCADE, related_name="runs")
def clean(self):
if self.signup.training != self.report.training:
# Note: adding an error code is best practice :)
raise ValidationError('some message', code='some_error_code')
EDIT #1: Not using ModelForm
You don't have to use the ModelForm to have validation on the model. Let's say you obtained Signup and Report instances from somewhere. Then you can instantiate and validate your Run instance like this:
run = Run(signup=signup, report=report)
# Raises ValidationErrors
run.full_clean()
# If all is fine, call save
run.save()

Report building in Django

I am trying to build a system which will enable my users to create their own reports based on the model of their choosing without me having to code them every time they need updating.
In order to do this, I've come up with the following models:-
from django.db import models
from django.contrib.contenttypes.models import ContentType
class ReportField(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
data_method = models.CharField(max_length=100)
def get_value_for(self, object):
return getattr(object, self.data_method)
class Report(models.Model):
name = models.CharField(max_length=200)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
data_fields = models.ManyToManyField(ReportField)
The idea is that users can create a Report based on the model they're interested in. They can then add any number of ReportFields to that report and, when the report runs, it will call the data_method (the name of a property) on each instance of the model in the db.
The bit I'm having trouble with is defining which properties the users can have access to. I need to have a way of creating a load of ReportFields with certain data_methods for each model. But I don't want to create them by hand - I want it to work in a similar way to the way Permissions work in Django, if that's possible, like this:-
class MyModel(models.Model):
class Meta:
data_methods = (
('property_name_1', 'Property Name 1'),
('property_name_2', 'Property Name 2'),
etc.
)
From reading the source code, Django seems to run a management command after every migration on that model to make sure the model permissions are created. Is that the only way to do this? Am I going in the right direction here, or is there a better way?

Which pattern to use for a model to have different variations of? Generic FK, multi-table, inheritance, others?

I am having trouble deciding how to structure my models for a particular data structure.
The models I have would be Posts, Groups, Users.
I want the Post model that can be posted from a groups page or user page and potentially more, like an events page.
Posts would contain fields for text, images(fk), user, view count, rating score (from -- a reference to where ever it was posted from like user or group page, though I am unsure how to make this connection yet)
I thought about using a Generic Foreign Key to assign a field to different models but read articles suggesting to avoid it. I tried the suggested models, but I wasn't unsure if they were the right approach for what I required.
At the moment I went with Alternative 4 - multi-table inheritance
class Group(models.Model):
name = models.CharField(max_length=64)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_groups')
members = models.ManyToManyField(
settings.AUTH_USER_MODEL)
def __str__(self):
return f'{self.name} -- {self.created_by}'
def save(self, *args, **kwargs):
# https://stackoverflow.com/a/35647389/1294405
created = self._state.adding
super(Group, self).save(*args, **kwargs)
if created:
if not self.members.filter(pk=self.created_by.pk).exists():
self.members.add(self.created_by)
class Post(models.Model):
content = models.TextField(blank=True, default='')
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_posts",
related_query_name="%(app_label)s_%(class)ss")
# class Meta:
# abstract = True
def __str__(self):
return f'{self.content} -- {self.created_by}'
class PostImage(models.Model):
image = models.ImageField(upload_to=unique_upload)
post = models.ForeignKey(
Post, related_name='images', on_delete=models.CASCADE)
def __str__(self):
return '{}'.format(self.image.name)
class UserPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_uPost', on_delete=models.CASCADE)
class GroupPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_gPost', on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
To do some specific filters ex:
Filter specific group post
Post.objects.filter(_gPost__group=group)
Filter specific user post
Post.objects.filter(created_by=user) # exclude groups with _gPost__isnull=False
Create post to user/group
p = Post.objects.create(...)
up = UserPost.objects.create(post=p)
gp = GroupPost.objects.create(post=p)
Really I am wondering if this is a sensible approach. The current way of a filter and creating feel odd. So only thing making me hesitant on this approach is just how it looks.
So, is Generic ForeignKey the place to use here or the current multi-table approach. I tried going with inheritance with abstract = True and that was unable to work as I need a foreign key to base post model. Even with no abstract, I got the foreign key reference, but filter became frustrating.
Edit:
So far only weird issues(but not really) are when filtering I have to be explicit to exclude some field to get what I want, using only .filter(created_by=...) only would get all other intermediate tables.
Filter post excluding all other tablets would requirePost.objects.filter(_uPost__isnull=True, _gPost__isnull=True, _**__isnull=True) which could end up being tedious.
I think your approach is sensible and that's probably how I would structure it.
Another approach would be to move the Group and Event foreignkeys into the Post model and let them be NULL/None if the Post wasn't posted to a group or event. That improves performance a bit and makes the filters a bit more sensible, but I would avoid that approach if you think Posts can be added to many other models in the future (as you'd have to keep adding more and more foreignkeys).
At the moment I will stick with my current pattern.
Some extra reading for anyone interested.
https://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/32-Polymorphic_Associations_Of_course_some

Django - 'WhereNode' object has no attribute 'output_field' error

I am trying to query and annotate some data from my models:
class Feed(models.Model): # Feed of content
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Piece(models.Model): # Piece of content (video or playlist)
removed = models.BooleanField(default=False)
feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Other fields are not used in the following queries so I skipped them here.
In my view I need to get queryset of all feeds of an authenticated user. Annotation should contain quantity of all pieces that are not removed.
Initially, Piece model didn't contain removed field and everything worked great with the queryset like this:
Feed.objects.filter(user=self.request.user).annotate(Count('piece'))
But then I added the field removed to Piece model and needed to count only pieces that were not removed:
Feed.objects.filter(user=self.request.user)
.annotate(Count('piece'), filter=Q(piece__removed=False))
It gave me the following error:
'WhereNode' object has no attribute 'output_field'
It is only a little fraction of what django outputs on the error page, so if it is not enough, please let me know what else I need to include in my question.
I tried to include output_field with options like models.IntegerField() or models.FloatField() (properly imported) here and there but got some errors which I do not provide here because I believe those actions made no sense.
I am using Django 2.0.3
Your syntax mistake is here,
Feed.objects.filter(user=self.request.user)
.annotate(Count('piece', filter=Q(piece__removed=False)))
filter needs to apply at Count not at annotate.
Reference from Django's documentation:
https://docs.djangoproject.com/en/2.1/topics/db/aggregation/#filtering-on-annotations

Django many-to-many lookup from different models

I have some models that represents some companies and their structure. Also all models can generate some Notifications (Notes). User can see own Notes, and, of course, can't see others.
class Note(models.Model):
text = models.CharField(...)
class Company(models.Model):
user = models.ForeignKey(User)
note = models.ManyToManyField(Note, blank='True', null='True')
class Department(models.Model):
company = models.ForeignKey(Company)
note = models.ManyToManyField(Note, blank='True', null='True')
class Worker(models.Model):
department = models.ForeignKey(Department)
note = models.ManyToManyField(Note, blank='True', null='True')
class Document(models.Model)
company = models.ForeignKey(Company)
note = models.ManyToManyField(Note, blank='True', null='True')
The question is how I can collect all Notes for particular user to show them?
I can do:
Note.objects.filter(worker__company__user=2)
But its only for Notes that was generated by Workers. What about another? I can try hardcoded all existing models, but if do so dozen of kittens will die!
I also tried to use backward lookups but got "do not support nested lookups". May be I did something wrong.
EDIT:
As I mentioned above I know how to do this by enumerating all models (Company, Worker, etc. ). But if I will create a new model (in another App for example) that also can generate Notes, I have to change code in the View in another App, and that's not good.
You can get the Notes of a user by using the following query:
For example let us think that a user's id is 1 and we want to keep it in variable x so that we can use it in query. So the code will be like this:
>>x = 1
>>Note.objects.filter(Q(**{'%s_id' % 'worker__department__company__user' : x})|Q(**{'%s_id' % 'document__company__user' : x})|Q(**{'%s_id' % 'company__user' : x})|Q(**{'%s_id' % 'department__company__user' : x})).distinct()
Here I am running OR operation using Q and distinct() at the end of the query to remove duplicates.
EDIT:
As I mentioned above I know how to do this by enumerating all models
(Company, Worker, etc. ). But if I will create a new model (in another
App for example) that also can generate Notes, I have to change code
in the View in another App, and that's not good.
In my opinion, if you write another model, how are you suppose to get the notes from that model without adding new query? Here each class (ie. Department, Worker) are separately connected to Company and each of the classes has its own m2m relation with Note and there is no straight connection to User with Note's of other classes(except Company). Another way could be using through but for that you have change the existing model definitions.
Another Solution:
As you have mentioned in comments, you are willing to change the model structure if it makes your query easier, then you can try the following solution:
class BaseModel(models.Model):
user = models.Foreignkey(User)
note = models.ManyToManyField(Note)
reports_to = models.ForeignKey('self', null=True, default=None)
class Company(BaseModel):
class Meta:
proxy = True
class Document(BaseModel):
class Meta:
proxy = True
#And so on.....
Advantages: No need to create separate table for document/company etc.
object creation:
>>c= Company.objects.create(user_id=1)
>>c.note.add(Note.objects.create(text='Hello'))
>>d = Document.objects.create(user_id=1, related_to=c)
>>d.note.add(Note.objects.create(text='Hello World'))