Django: Querying many-to-one model instance fields - django

I've been playing around in the Django tutorial and am trying to get the field of a model instance associated (via foreign key) to another model instance. Here is what I mean:
Model file:
from django.db import models
class Usercar(models.Model):
make = models.CharField(max_length=200)
model = models.CharField(max_length=200)
year = models.DateTimeField('year of car')
def __unicode__(self):
return self.make
class Suggestcar(models.Model):
usercar = models.ForeignKey(Usercar)
make = models.CharField(max_length=200)
model = models.CharField(max_length=200)
year = models.DateTimeField('year of car')
def __unicode__(self):
return self.make
In the shell, this is what I get when I try to get the make of a suggestcar related to a usercar:
>>from django.utils import timezone
>>mycar1 = Usercar(make="Ford",model="Escort",year=timezone.now())
>>mycar1.save()
>>mycar1.suggestcar_set.create(make="Ford", model="Taurus", year=timezone.now())
>>mycar1.suggestcar_set.filter(id=1).make
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site- packages/django/core/management/commands/shell.pyc in <module>()
----> 1 mycar1.suggestcar_set.filter(id=1).make
AttributeError: 'QuerySet' object has no attribute 'make'
How can I get the make of this mycar1.suggestcar_set.filter(id=1)? Thanks!

filter returns a queryset, if you want to access the objects in it, you need to iterate over the queryset or get the first result from it.
mycar1.suggestcar_set.filter(id=1)[0].make
If you want to return just 1 object, you should really use get
mycar1.suggestcar_set.get(id=1).make

Related

ManyToManyField with model_to_dict(self)

Not sure if the title is the correct one, sorry for the inconvenience.
I'm having a problem on sending a ManyToManyField from a model to a dictionary using the model_to_dict() Below is my code
models.py
from django.db import models
from django.forms import model_to_dict
from app_1.models import *
class Stuff(models.Model):
thing_1 = models.CharField(max_length=100, null=True, blank=True)
thing_2 = models.ManyToManyField(OtherStuff, blank=True, related_name="thing")
def toJSON(self):
item = model_to_dict(self)
item['thing'] = self.thing.toJSON()
return item
When I run a query and load my Stuff model, I get the following error:
from app_2.models import *
s = Stuff.objects.get(pk=1)
# here is where I send my model to a dictionary
s.toJSON()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "P:\test\app\app_2\stuff\models.py", line 10, in toJSON
return item
AttributeError: 'ManyRelatedManager' object has no attribute 'toJSON'
I've come across multiple ways of sending a ManyToManyField to a dictionary, however, none of them use the model_to_dict(). I'd like to use this method due to it's simplicity of usage.
With this approach, you'll need to use this syntax:
item['thing'] = [t.toJSON() for t in self.thing_2.all()]
And also, implement the toJSON method for the OtherStuff model.
Or you can use model_to_dict for OtherStuff as well:
def toJSON(self):
item = model_to_dict(self)
item['thing'] = [model_to_dict(t) for t in self.thing_2.all()]
return item

Django models.FileField(upload_to=function1, default=function2)

I'm trying to set the default image in a django model with a function. Te reason is that i generate the image (a qrcode) and i want to save it with a model asociated with the request.user, it is working with the upload_to= but fails with the default, it seems it doesn't get the instance.
The code is:
-- models.py --
from django.db import models
from django.contrib.auth.models import User
import django.utils.timezone as timezone
from myproject.settings import MEDIA_ROOT
import os
def function1(instance, filename):
retun os.path.join(self.uid.username, str(instance.shortname), '.jpg')
def function2(instance):
return os.path.join(MEDIA_ROOT, str(instance.username), 'tmp.jpg')
class MyModel(models.Model):
uid = models.ForeignKey(User)
shortname = models.CharField()
qr = models.FileField(upload_to=function1, default=function2)
this gives the function2() takes exactly 1 argument (0 given)
i also tried to pass uid as parameter to function2 like this:
def function2(uid):
username = uid.username
return os.path.join(MEDIA_ROOT, username, 'tmp.jpg')
class MyModel(models.Model):
uid = models.ForeignKey(User)
shortname = models.CharField()
qr = models.FileField(upload_to=function1, default=function2(uid))
but this won't work too, gives an AttributeError exception:
'ForeignKey' object has no attribute 'username'
Any ideas on how can i tell django to upload a locally generated file (generated on the django machine at MEDIA_ROOT/request.user.username/)
Thanks!!
I found a way to solve my problem from views here:
Loading Django FileField and ImageFields from the file system
You need to be using instance instead of self. Also, function2 will not be being passed an instance.
You need to use instance instead of self. self does not exist in function1 and function2 scope:
def function1(instance, filename):
return os.path.join(instance.uid.username, str(instance.shortname), '.jpg')
def function2(instance):
return os.path.join(MEDIA_ROOT, str(instance.username), 'tmp.jpg')
class MyModel(models.Model):
uid = models.ForeignKey(User)
shortname = models.CharField()
qr = models.FileField(upload_to=function1, default=function2)
You can found docs for FileField.upload_to here
You get function2() takes exactly 1 argument, (0 given) because function2 is expecting for instance param, and if you try to pass uid like this:
class MyModel(models.Model):
uid = models.ForeignKey(User)
shortname = models.CharField()
qr = models.FileField(upload_to=function1, default=function2(uid))
will raise an error because at this point uid is a models.ForeignKey instance. You could try to do this in a pre_save signal:
def function2(sender, instance, *args, **kwargs):
if not instance.qr:
instance.qr = os.path.join(MEDIA_ROOT, username, 'tmp.jpg')
pre_save.connect(function2, sender=MyModel)

Non-queryset data ordering in django-tables2

The docs say:
Where the table is backed by a model, the database will handle the ordering. Where this is not the case, the Python cmp function is used and the following mechanism is used as a fallback when comparing across different types: ...
But is this possible in a table that is backed by a model, on a custom column? e.g.
class MyModel(models.Model):
x = models.IntegerField()
y = models.IntegerField()
def z(self):
return x+y
class MyTable(tables.Table):
z = tables.Column()
class Meta:
model = MyModel
When I try something like this, the column displays OK, but when I click on the column header to sort, I get this error:
Caught FieldError while rendering: Cannot resolve keyword u'z' into field. Choices are: ...
Apparently this is because z is not found in the database table.
Is there a way around this?
You can't use a queryset if you're ordering on an attribute that doesn't have a database column. You can pass a list to your table though.
Assuming your models.py looks like this:
from django.db import models
class MyModel(models.Model):
def foo(self):
return something_complex()
You could have tables.py that looks like this:
import django_tables2 as tables
from .models import MyModel
class MyModelTable(tables.Table):
foo = tables.Column()
class Meta:
model = MyModel
Then in your views.py:
from django_tables2.config import RequestConfig
from django.core.paginator import InvalidPage
from django.shortcuts import render
def view_my_models(request):
# use a list so django_tables2 sorts in memory
my_models = list(MyModel.objects.all())
my_models_table = MyModelTable(my_models)
RequestConfig(request).configure(my_models_table)
try:
page_number = int(request.GET.get('page'))
except (ValueError, TypeError):
page_number = 1
try:
my_models_table.paginate(page=page_number, per_page=10)
except InvalidPage:
my_models_table.paginate(page=1, per_page=10)
template_vars = {'table': my_models_table}
return render(response, "view_my_models.html", template_vars)
There's also an open ticket discussing this issue.

Django: Accessing Many to Many object through another Many to Many relationship

I have simplified my models down a to make it clearer what I am trying to do.
(models.py in app Teams)
from django.db import models
from django.contrib.auth.models import User
import datetime
class Team(models.Model):
users = models.ManyToManyField(User)
team_title = models.CharField(max_length=200)
team_description = models.CharField(max_length=200)
def __unicode__(self):
return self.team_title
(models.py in app Documents)
from django.db import models
import datetime
class Document(models.Model):
teams = models.ManyToManyField("Teams.Team", blank=True)
document_title = models.CharField(max_length=200)
document_description = models.TextField()
def __unicode__(self):
return self.document_title
What I want to achieve is getting a list of users who have are associated with a Document by first getting all the teams associated with the document and then from this getting all the users associated with those teams.
My attempts so far have gone something like this
(view.py in app Documents)
from django.contrib.auth.models import User
from Documents.models import *
from Teams.models import *
def docUsers(request, doc_id):
current_document = Documents.objects.get(pk = doc_id)
associated_users = current_document.teams.all().users
....
Error: 'QuerySet' object has no attribute 'users'
associated_users = current_document.items.all().users.all()
Error: 'QuerySet' object has no attribute 'users'
associated_users = current_document.items.users.all()
Error: 'ManyRelatedManager' object has no attribute 'users'
Am I going about this the wrong way?
Well, yes. current_document.teams.all() is a queryset - more or less, a list - of Teams. It doesn't make sense to ask for current_document.teams.all().users, as a queryset doesn't itself have a 'users' attribute, hence the error. users is an attribute of each Team element within that queryset. So, one way of doing it would be to iterate through the queryset and ask for the users associated with each team.
However, that would be hopelessly inefficient - one database call for each team. A much better way is to ask the database directly: give me all the users who are in teams associated with the current document. Like this:
User.objects.filter(team__documents=current_document)

Django: "ZeroToOneField" DoesNotExist Error

I'm trying to determine the best way to make a "zero-to-one" relationship between two models. For example, a model named Post can have zero or one related model instances of a Model called PostExtra. I'd like the reverse to be true as well.
from django.db import models
class PostExtra(models.Model):
author = models.CharField(max_length=64)
active = models.BooleanField(default=False)
"""
Assigned a property to prevent DoesNotExist error when calling
self.post, but property does not override self.post properly
for some reason.
"""
def _get_post(self):
return_value=None
try:
return_value = self.post
except:
pass
return return_value
def _set_post(self, post):
self.post = post
post = property(_get_post, _set_post)
def __unicode__(self):
return "%s" % (self.author)
class Post(models.Model):
title = models.CharField(max_length=64)
text = models.TextField()
extra = models.OneToOneField('a.PostExtra', blank=True, null=True)
def __unicode__(self):
return "%s" % (self.title)
Here, I can create a Post()
>>> p = Post(title="test 1", text="test text")
>>> p.save()
>>> p.extra # this returns None as it should
As above, since I made Post.extra a OneToOneField with blank=True/null=True, p.extra will return Null if no PostExtra is assigned. However, if I do the reverse and try to access PostExtra.post, I get a DoesNotExist error.
>>> pe = PostExtra(author='John Doe')
>>> pe.save()
>>> pe.post
...
DoesNotExist: Post matching query does not exist.
I tried assigning a property on PostExtra to override the PostExtra.post using a property, but I still get the error. Has anyone found a way to get OneToOneFields to not throw an exception (and return Null) when trying to access a non-existent related element?
Any advice is much appreciated.
You would need to specify a different related_name on your relationship in order to make this type of code work.
from django.core.exceptions import ObjectDoesNotExist
class PostExtra(models.Model):
pass ## Brevity.
def _get_post(self):
return_value=None
try:
return_value = self._post
except ObjectDoesNotExist: ## Be explicit if you can.
pass
return return_value
def _set_post(self, post):
self._post = post
post = property(_get_post, _set_post)
class Post(models.Model):
pass ## Brevity.
extra = models.OneToOneField('a.PostExtra', blank=True,
null=True, related_name='_post')
You can then access the post in a few different ways:
>>> pe = PostExtra(author='John Doe')
>>> pe.save()
>>> pe.post
None
>>> pe._post
DoesNotExist: Post matching query does not exist.
Ninja Edit:
The question may be raised, "Why do I have to do it this way?". The answer is because when the Django model classes set up your PostExtra object, it is creating PostExtra.post as a reference back to Post. I'm not familiar with the code itself, but I doubt it checks to make sure the coast is clear before doing the assignment, since the programmer told it to make that relation. As a result, you have to specify a non-conflicting property for Django's model classes to use, thus the related_name argument.