I have the following two models:
class URLResource(models.Model):
class ResourceType(models.TextChoices):
VID = 'VID', _('Video')
DOC = 'DOC', _('Document')
resource_type = models.CharField(
choices=ResourceType.choices,
default=ResourceType.UNK,
max_length=3)
title = models.CharField(max_length=280, null=True)
url = models.URLField()
created = models.DateTimeField(auto_now_add=True)
def __repr__(self):
return f'URLResource {self.resource_type}: {self.title} URL: {self.url}'
class Record(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
...)
date = models.DateTimeField(default=now, blank=True)
# <resource>: I want to point to a resource somehow
def __repr__(self):
return f'Record {{ User: {self.user}, Resource: }}'
My use case is such that a lot of users can create Records and list them, but since the underlying URLResource is going to be same, I thought I could use many-to-one relationship from Records-to-URLResource so the underlying resources would remain the same. However, even though I have only one type of resource now i.e. URLResource, I also want to roll out other resources like VideoResource, PhysicalResource etc. Clearly, I can't simply use the many-to-one relationship. So, I figured I could use Django's contenttype framework, but I still can't get things to work.
I want to be able to do atleast these two things:
When creating a Record, I can easily assign it a URLResource (or any other Resource) (but only one resource).
I can easily access the URLResource fields. For example like Record.objects.get(user=x).resource.resource_type.
Related
How can I concatenate two different model query and order by a field that both models has like progress fields.
For example
models.py
class Gig(models.Model):
author= models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
class Project(models.Model):
author= models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
Can I do my view.py like this, for me to achieve it?
İf No, How can I achieve it?
views.py
def fetch_all_item(request):
gig = Gig.objects.filter(author_id = request.user.id)
project = Project.objects.filter(author_id = request.user.id)
total_item = (gig + project).order_by("progress")
return render(request, "all_product.html", {"item": total_item})
I am trying to join two query set from Gig and Project models then send it to frontend in an ordering form by a field name called progress.
You can let Python do the sorting for you, like:
from operator import attrgetter
def fetch_all_item(request):
gig = Gig.objects.filter(author=request.user)
project = Project.objects.filter(author=request.user)
total_item = sorted([*gig, *project], attrgetter('progress'))
return render(request, "all_product.html", {'item': total_item})
It might however be better to remodel this to a single model with a type field that disambiguates between a Gig and a Project.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
In general, such design when there are common fields is better accomplished by inheriting from some base class. E.g.:
class CommonActivity(models.Model):
# common fields for Project/Gig
author = models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
class Gig(CommonActivity):
pass # or other fields related to Gig only
class Project(CommonActivity):
pass
Then if you want to query both - you query CommonActivity.
If remodel not possible - then filter via Python as #WillemVanOnsem suggested.
However such filtering will be way less efficient
I have the two following classes:
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
class Source(models.Model):
sid = models.IntegerField(primary_key=True)
incident = models.ForeignKey('Incident', on_delete=models.SET_NULL, null=True)
url = models.TextField(validators=[URLValidator()])
datereported = models.DateField(null=True, blank=True)
I want to create a field within the Incident that will pull the minimum datereported of related sources. Is this best done in the model, or in the template? Unsure what best practice is, or how to execute in this case.
Is this best done in the model, or in the template?
Well a template should - strictly speaking - not contain business logic. It should contain render logic. It should thus specify how something should be visible, not what should be visible. So it dos not really belong in the template layer, only in the model layer.
You can obtain the smallest datereported with:
from django.db.models import Min
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
#property
def first_reporteddate(self):
return self.source_set.aggregate(first=Min('datereported'))['first']
This will ignore Sources with datereported set to None (so if there are multiple sources, it takes the smallest datereported that is not None). If there are no Source with a datereported not equal to None, or no related Sources at all, it will return None however, since the minimum of an empty set is considered to be NULL (in SQL, or None in Python/Django).
You can then use this in the template like:
{{ some_incident.first_reporteddate }}
In case you want the entire object, you can use self.source_set.order_by('datereported').first() which will give you the earliest related Source instance. But this will have a (quite) small impact on performance (it will take a bit longer). In that case Django will fetch all columns into memory first. If you thus only need one column, this will result in the fact that you did some useless serialization (at the database end) and deserialization (at the Python end).
You can use model's property for this:
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
#property
def first_datereported(self):
first_source = self.source_set.order_by('datereported').first()
if first_source:
return first_source.datereported
In template or in any other part of code you can use first_datereported as normal model's field:
{{ incident_instance.first_datereported }}
In my Django application, I've got two models, one Users and one Friendships. There is a Many to Many relationship between the two, as Users can have many Friends, and Friends can have many other Friends that are Users.
How can I return all friends (first and last name) whom are NOT friends with the user with the first_name='Daniel'?
Models.py:
class Friendships(models.Model):
user = models.ForeignKey('Users', models.DO_NOTHING, related_name="usersfriend")
friend = models.ForeignKey('Users', models.DO_NOTHING, related_name ="friendsfriend")
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'friendships'
class Users(models.Model):
first_name = models.CharField(max_length=45, blank=True, null=True)
last_name = models.CharField(max_length=45, blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'users'
So far, here's what I've tried in my controller (views.py) -- please note, I understand controllers should be skinny but still learning so apologies. What I tried in the snippet below (after many failed attempts at a cleaner method) was to try and first grab friends of daniels (populating them into a list and then removing any duplicate ids), and then filter them out by their id.
# show first and last name of all friends who daniel is not friends with:
def index(req):
friends_of_daniel = Friendships.objects.filter(user__first_name='Daniel')
daniels_friends = []
for friend_of_daniel in friends_of_daniel:
daniels_friends.append(friend_of_daniel.friend.id)
daniels_friends = list(set(daniels_friends))
not_daniels_friends = Friendships.objects.exclude(id__in=daniels_friends)
context = {
'not_daniels_friends':not_daniels_friends,
}
return render(req, "friendapp/index.html",context)
However, when I try the following in my views (templates) file, I still see individuals whom are friends of Daniels. Any idea what I'm doing wrong?
<ul>
{% for not_daniel_friend in not_daniels_friends %}
<li>{{ not_daniel_friend.user.first_name }} {{ not_daniel_friend.user.last_name }}</li>
{% endfor %}
</ul>
I guess something like this will do. Just then take the list users, and get the first and last name of those users.
daniels = Users.objects.filter(first_name="Daniel") # There may be more than one Daniel
users = Friendships.objects.exclude(friend__in=daniels)
Note here, while Friendships.friend is a foreignkey of type Users you can pass Users instances (i.e daniels list) in friend__in to exclude those users.
Try this,In the place of friend_of_daniel.friend.id , You should exclude the results from User model.
Something like this :
def index(req):
friends_of_daniel = Friendships.objects.filter(user__first_name='Daniel')
daniels_friends = []
for friend_of_daniel in friends_of_daniel:
daniels_friends.append(friend_of_daniel.friend.id)
daniels_friends = list(set(daniels_friends))
not_daniels_friends = Users.objects.exclude(id__in=daniels_friends)
context = {
'not_daniels_friends':not_daniels_friends,
}
return render(req, "friendapp/index.html",context)
Thanks.
Firstly as a general comment: a cleaner way of populating a list of ids is using the .value_list() method from django (part of the .values() method in previous versions of Django). It has a "flat" flag that creates the list you want.
So, instead of:
friends_of_daniel = Friendships.objects.filter(user__first_name='Daniel')
daniels_friends = []
for friend_of_daniel in friends_of_daniel:
daniels_friends.append(friend_of_daniel.friend.id)
daniels_friends = list(set(daniels_friends))
You could do, in one line:
daniels_friends = Friendships.objects \
.filter(user__first_name='Daniel') \
.distinct('friend') \
.values_list('friend', flat=True)
distinct makes the same as your list() - set() cast (it makes sure that your list has no repeated elements) and values_list with flat=True can be customizable to any field in the related "user" table: .values_list('friend__id', flat=True) or .values_list('friend__first_name', flat=True) to get a list of first_names of Daniel's friends.
Coming back to your general question, you can do the whole query directly in one line using your related_names, as I am not really sure of what you want (an user instance, a Friendship instance or just a list of firsts and last names) I will give you many options:
If you want a Friendship instance (what you are trying in your sample
code):
friendships_not_friends_with_daniel = Friendships.objects\
.exclude(friend__first_name="Daniel")
This is equivalent to what #Rafael proposes in his answer:
daniels = Users.objects.filter(first_name="Daniel") # There may be
more than one Daniel users =
Friendships.objects.exclude(friend__in=daniels)
Here I am embedding his first query in the exclude by referencing the
field in the related table with double underscore (which is an very
powerful standard in Django).
If you want an User instance:
users_with_no_friendship_with_daniel = Users.objects\
.exclude(usersfriend__friend__first_name="Daniel")
Here you are using the related name of your model to access from the
users table to the friendships table, and then check if the friend of this user is called Daniel. This way of querying is a bit complex to understand but as soon as you get used to it becomes really powerful because it is very similar to the spoken language: you want all users, but excluding the ones that have a friendship, whose friend's first name is Daniel. Depending on how many friends an user hat or how many users are called Daniel, you might to add some distinct() methods or split the query in two.
As an advice, maybe you could improve the related name in your model, because it is what you would use if you have an user instance and want to get the related friendships: user_instance.friendships instead of user_instance.usersfriend and user_instance.friendsfriendships instead of user_instance.friendsfriend.... Do not know, it is always difficult to me to choose good related names...
If you want a list of tuples of users first and last names:
names_of_users_with_no_friendship_with_daniel = Users.objects\
.exclude(usersfriend__friend__first_name="Daniel")\
.values_list('first_name', 'last_name')
I am sorry if something is not clear, please ask and I try to explain better. (I am quite new in stackoverflow)
Here are stripped down versions of the models I'm dealing with:
class Contact(models.Model):
contact_no = models.IntegerField(primary_key=True)
email_address = models.CharField(max_length=60, null=True)
class ContactRole(models.Model):
contact_no = models.ForeignKey(Contact, primary_key=True, db_column='contact_no')
role_code = models.CharField(primary_key=True, max_length=16)
role_scope_code = models.CharField(primary_key=True, max_length=16)
Contacts can and almost always do have many ContactRoles.
I want a list of Contacts where the role_scope_code of the related ContactRole is 'foo'. I know I can get this with:
Contact.objects.filter(contactrole__role_scope_code='foo')
What I also want, is for each Contact in the queryset to have a single .contactrole property. It would be the ContactRole with the role_scope_code of 'foo'. Instead I'm getting a set of all ContactRoles that match on contact_no, so that to get to properties of the ContactRole I have to do something like this:
contacts = Contact.objects.filter(contactrole__role_scope_code='foo')
for contact in contacts:
print contact.contactrole_set.filter(role_scope_code='foo')[0].role_code
I have to filter on role_scope_code twice! That doesn't seem DRY at all. What I'm looking for is a query that will allow me to have a set that works like this:
contacts = Contact.objects.filter(contactrole__role_scope_code='foo')
for contact in contacts:
print contact.contactrole.role_code
For the life of me I can't figure out how to tell Django to only return the related objects that match the filter I applied to the parent object.
A OneToOneField will solve this provided that a contact only have one contactrole. A OneToOneField gives you the api you are looking for. So instead of using a ForeignKey use a OneToOneField
Google has lots of examples of doing reverse lookup in the interactive prompt but none of doing them inside a django model as a method.
I have the following models.py file:
class Client(models.Model):
...
def __unicode__(self):
return ???
class ClientDetails(models.Model):
client = models.ForeignKey(Client, null=True)
created = models.DateTimeField(default=datetime.now)
created_by = models.ForeignKey(User, null=True)
name_title = models.CharField(max_length=3, choices=NAME_TITLE_CHOICES)
first_name = models.CharField(max_length=40)
middle_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
...
How do I get the client method to return last_name from ClientDetails?
If a ClientDetails object should only ever be associated with a single Client object, then I would change your FK to a OneToOneField, which will provide you with a neat reverse accessor that can only ever link between a given Client and its associated ClientDetails. Then you can do:
try:
return self.clientdetails.last_name
except ClientDetails.DoesNotExist:
##handle it here, returning a graceful message
Alternatively, if you kept it as a FK, then you'd have to do something like:
try:
self.clientdetails_set.all()[0].last_name
except IndexError, e:
## handle exception here
but using a FK here is brittle and not great form (as the exception-handling implies: if there is none returned, then you'll get an IndexError. Also, there might be more than one ClientDetails object linked to that Client, and you'd only get the details of the first one here.)
So, I really would recommend using a OneToOneField instead of that FK. (All a OneToOneField is basically an FK with unique=True set on it and some neater accessors than standard FKs)
For that you can use in client the *clientdetails_set* to access all the ClientDetails objects that are linked to that client.
The set is an object set from Django, so calling the method all() will retrieve every of the objects. If you know there's only one you can do self.clientdetails_set.all()[0].last_name
Here's the link for the django documentation: Link