I am using the following models for my first django site. But I am currently having problems with how to access the wishes of a user.
class Group(models.Model):
title = models.CharField(max_length=100)
users = models.ManyToManyField(User, related_name='group_users')
description = models.TextField()
added = models.DateTimeField()
owner = models.ForeignKey(User)
def __unicode__(self):
return self.title
class Wish(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User, related_name='wish_user')
bought = models.IntegerField(default=0)
bought_by = models.ForeignKey(User, related_name='wish_buyer')
added_by = models.ForeignKey(User, related_name='wish_adder')
cost = models.FloatField()
added = models.DateTimeField()
def __unicode__(self):
return self.name
def is_bought(self):
return self.bought % 2 == 1
def is_editable(self):
return self.added >= timezone.now() - datetime.timedelta(hours=1)
When I go to the django shell I get the following:
$ ./manage.py shell
>>> from django.contrib.auth.models import User
>>> from wish.models import Wish, Group
>>> user1 = User.objects.filter(id=1)[0]
>>> user1.group_set.all()
[]
>>> user1.wish_set.all()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'User' object has no attribute 'wish_set'
>>>
Why doesn't the User object get the wish_set like it does get the group_set ?
That's because you renamed them to wish_user, wish_buyer and wish_adder. Whereas for the group you have wish_set implicity from the owner property and the explicit group_users.
The related_name parameter tells Django how to name the reverse relation. If it's not given it will be <field name>_set
Related
I am using the django shell to look at some querysets. I am able to retrieve all of my Student objects with .all() but if I then try to retrieve one object with .get() or filter on a field, I'm getting empty returns. The project seems to run ok on the browser (I'm still in the progress of writing all my tests but functionally it is working). I am able to successfully use get() or filter on a field with my Objectivemodel.
Here is what I get with all():
>>> from gradebook.models import Student, Objective
>>> s = Student.objects.all()
>>> for i in s:
... print(i.student_first)
...
John
Sally
Keith
Terrance
Henry
Analise
Ron
>>>
Now if I try to use get():
>>> qs = Student.objects.get(student_first='Analise')
Traceback (most recent call last):
gradebook.models.Student.DoesNotExist: Student matching query does not exist.
Or filter on a field:
>>> s.filter(student_first='Ron')
<QuerySet []>
>>>
Student model
class Student(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
student_first = models.CharField(max_length=30)
student_last = models.CharField(max_length=30)
nickname = models.CharField(max_length=31)
fullname = models.CharField(max_length=60)
attend = models.BooleanField(default=True)
student_number = models.IntegerField()
email = models.EmailField(max_length=50)
def __str__(self):
return self.fullname
I have a many-to-many self-referencing relationship that includes a through model. Products are composed of other Products, including an amount (I.E a screw is made from iron rods). I made the through model, although I can't seem to access the 'amount' field in the through model:
Model code:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200, unique=True)
produced = models.IntegerField(default=0)
speed = models.IntegerField()
type = models.CharField(max_length=200)
ingredients = models.ManyToManyField('self', through='RecipeComponent', symmetrical=False)
def __str__(self):
return self.name
class RecipeComponent(models.Model):
item = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="item")
ingredient = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="ingredient")
amount = models.IntegerField()
def __str__(self):
return str(self.amount) + " of " + self.ingredient.name
I've tried a number of other queries, but as you can see they seem to fail. I know I'm missing something but I can't put my finger on it.
screw.ingredients.all()
>>> <QuerySet [<Product: iron_rod>]>
screw.ingredients.all()[0].amount
>>> Traceback (most recent call last):
>>> File "<input>", line 1, in <module>
>>> AttributeError: 'Product' object has no attribute 'amount'
screw.ingredients.all()[0]
Is a product, and if you try:
screw.ingredients.all()[0].amount
you're trying to access the amount attribute of Product which does not have. Hence the error:
>>> AttributeError: 'Product' object has no attribute 'amount'.
If you want to get the amount describing some relation you can:
relation_data = RecipeComponent.objects.get(product=screw, ingredient=wood)
relation_data.amount # The amount of wood for screw.
Or you want to know how many different ingredients has screw:
RecipeComponent.objects.filter(product=screw).count()
You can create a method within your Product class that returns the amount of the given ingredient:
class Product(models.Model):
...
...
def get_amount_of_ingredient(self, ingredient):
try:
return RecipeComponent.objects.get(item=self, ingredient=ingredient).amount
except RecipeComponent.DoesNotExist:
return 0
Then call it from screw object:
screw.get_amount_of_ingredient("iron_rod")
I have function with signal:
#receiver(post_save, sender=Task)
def my_handler():
executor = User.objects.filter(user_type='Executer')
executor.balance += Task.money
executor.save()
My function should add money to executor after task added. But it gives mistake like:
Internal Server Error: /api/v1/tasks/
Traceback (most recent call last):
...
File "/home/k/pro/freelance-django/free/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 1853, in get_prep_value
return int(value)
ValueError: invalid literal for int() with base 10: 'Executer'
[20/Sep/2017 14:00:30] "POST /api/v1/tasks/ HTTP/1.1" 500 188088
User class looks like:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
CUSTOMER = 1
EXECUTER = 2
USER_TYPES = (
(CUSTOMER, 'Customer'),
(EXECUTER, 'Executer'),
)
user_type = models.IntegerField(choices=USER_TYPES, default=EXECUTER, verbose_name='Тип пользователя')
balance = models.DecimalField(decimal_places=2, max_digits=7, default=0, verbose_name='Баланс')
def __str__(self):
return self.username
Task class from task.models is:
class Task(models.Model):
title = models.CharField(max_length=255, verbose_name='Заголовок')
description = models.CharField(max_length=255, verbose_name='Описание')
money = models.DecimalField(max_digits=7, decimal_places=2, default=0, verbose_name='Цена')
assignee = models.ForeignKey('users.User', related_name='assignee', null=True, verbose_name='Исполнитель')
created_by = models.ForeignKey('users.User', related_name='created_by', verbose_name='Кем был создан')
How should I make it work?
U don't have one object but objects so you need literal on them or do update(). The best option is use F expression which has a good performance because its executed on DB:
from django.db.models import F
#receiver(post_save, sender=Task)
def my_handler(sender, instance, **kwargs):
User.objects.filter(user_type=User.EXECUTER).update(balance=F('balance') + instance.money)
This update all records where user_type is Executer and update them balance in one query.
Django docs update
F expressions
Your user_type field is IntegerField and in your choices you have string value so u need change it to something like this:
class User(AbstractUser):
CUSTOMER = 1
EXECUTER = 2
USER_TYPES = (
(CUSTOMER, 'Customer'),
(EXECUTER, 'Executer'),
)
user_type = models.IntegerField(choices=USER_TYPES, default=EXECUTER, verbose_name='Тип пользователя')
balance = models.DecimalField(decimal_places=2, max_digits=7, default=0, verbose_name='Баланс')
Is it possible to have a field in a Django model which does not get stored in the database.
For example:
class Book(models.Model):
title = models.CharField(max_length=75)
description models.CharField(max_length=255, blank=True)
pages = models.IntegerField()
none_db_field = ????
I could then do
book = Book.objects.get(pk=1)
book.none_db_field = 'some text...'
print book.none_db_field
Thanks
As long as you do not want the property to persist, I don't see why you can't create a property like you described. I actually do the same thing on certain models to determine which are editable.
class Email(EntryObj):
ts = models.DateTimeField(auto_now_add=True)
body = models.TextField(blank=True)
user = models.ForeignKey(User, blank=True, null=True)
editable = False
...
class Note(EntryObj):
ts = models.DateTimeField(auto_now_add=True)
note = models.TextField(blank=True)
user = models.ForeignKey(User, blank=True, null=True)
editable = True
Creating a property on the model will do this, but you won't be able to query on it.
Example:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def _get_full_name(self):
return "%s %s" % (self.first_name, self.last_name)
def _set_full_name(self, combined_name):
self.first_name, self.last_name = combined_name.split(' ', 1)
full_name = property(_get_full_name)
full_name_2 = property(_get_full_name, _set_full_name)
Usage:
from mysite.models import Person
a = Person(first_name='John', last_name='Lennon')
a.save()
a.full_name
'John Lennon'
# The "full_name" property hasn't provided a "set" method.
a.full_name = 'Paul McCartney'
Traceback (most recent call last):
...
AttributeError: can't set attribute
# But "full_name_2" has, and it can be used to initialise the class.
a2 = Person(full_name_2 = 'Paul McCartney')
a2.save()
a2.first_name
'Paul'
To make it an instance variable (so each instance gets its own copy), you'll want to do this
class Book(models.Model):
title = models.CharField(max_length=75)
#etc
def __init__(self, *args, **kwargs):
super(Foo, self).__init__(*args, **kwargs)
self.editable = False
Each Book will now have an editable that wont be persisted to the database
If you want i18n support:
# Created by BaiJiFeiLong#gmail.com at 2022/5/2
from typing import Optional
from django.db import models
from django.utils.translation import gettext_lazy as _
class Blog(models.Model):
title = models.CharField(max_length=128, unique=True, verbose_name=_("Title"))
content = models.TextField(verbose_name=_("Content"))
_visitors: Optional[int] = None
#property
def visitors(self):
return self._visitors
#visitors.setter
def visitors(self, value):
self._visitors = value
visitors.fget.short_description = _("Visitors")
In the following code, I'm tyring to create the method show_pro that will show all Arguments for the Case that are pro.
I am getting this error:
>>> Case.objects.all()[0].show_pro()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/john/mysite/../mysite/cases/models.py", line 23, in show_pro
return self.objects.filter(argument__side__contains='p')
File "/usr/lib/python2.5/site-packages/django/db/models/manager.py", line 151, in __get__
raise AttributeError, "Manager isn't accessible via %s instances" % type.__name__
AttributeError: Manager isn't accessible via Case instances
here is the code:
from django.db import models
from django.contrib.auth.models import User
import datetime
SIDE_CHOICES = (
('p', 'pro'),
('c', 'con'),
('u', 'undecided'),
)
class Case(models.Model):
question = models.CharField(max_length=200)
owner = models.ForeignKey(User)
pub_date = models.DateTimeField('date published')
rating = models.IntegerField()
def __unicode__(self):
return self.question
def was_published_today(self):
return self.put_date.date() == datetime.date.today()
def show_pro(self):
return self.objects.filter(argument__side__contains='p')
class Argument(models.Model):
case = models.ForeignKey(Case)
reason = models.CharField(max_length=200)
rating = models.IntegerField()
owner = models.ForeignKey(User)
side = models.CharField(max_length=1, choices=SIDE_CHOICES)
def __unicode__(self):
return self.reason
Try:
def show_pro(self):
return self.argument_set.filter(side='p')
Basically, you need to do a reverse lookup on the ForeignKey relationship and then filter that to get the related Argument objects that have side='p'.
In the show_pro function, self is not a QuerySet—it refers to the object itself.
You can't call self.objects, objects is a class member, not an field on the instance. THink about it this way, would it make sense to do:
c0 = Case.objects.all()[0]
c1 = c0.objects.all()[1]
Not really. That's what using self does.
Instead, you need to access the instance fields. As Tyson suggested:
class Case(models.Model):
...
def show_pro(self):
return self.argument_set.filter(side='p')
...