I have two django models
class ValidName:
name = models.TextField()
class MetaSyntacticName(ValidNames):
name = models.ForeignKey(ValidName)
usages = models.IntegerField()
If I have an instance of MetaSyntacticName, can I find out if the ValidName instance it's name references has been loaded from the database without a database query?
One way to do this I could find is using a private model instance attribute _state. It has an attribute fields_cache which is a mapping: field name -> field cache.
So in your case you can check if a foreign key name has been loaded using this line:
'name' in instance._state.fields_cache
where instance is an instance of MetaSyntacticName.
The following code demonstrates that it's working:
foo = ValidName.objects.create(name='foo')
foo_meta = MetaSyntacticName.objects.create(name=foo, usages=1)
'name' in foo_meta._state.fields_cache # True
foo_meta = MetaSyntacticName.objects.get(name_id=foo.id)
'name' in foo_meta._state.fields_cache # False
# next line hits the db and loads the field 'name'
foo_meta.name
'name' in foo_meta._state.fields_cache # True
It took some time for me to hack this out so I hope this saves someone's time :)
P.S. I checked that the similar code works on Django 2.2, as _state is a private attribute it might be different between Django versions
If you call select_related, then there won't be any extra database query for prepopulating ForeignKey related objects. For example:(copy pasted from documentation):
# Hits the database.
e = Entry.objects.get(id=5)
# Hits the database again to get the related Blog object.
b = e.blog
And here’s select_related lookup:
# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)
# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog
You can add method like this:
class MetaSyntacticName(ValidNames):
...
def valid_name_is_cached(self):
return __class__.validname_ptr.is_cached(self)
__class__ is just MetaSyntacticName
validname_ptr - is Django descriptor which have method is_cached
Related
I am a newbie to django and was reading about select_related. I understand that whenever a foreign key is accessed django executes an additional query. But when I checked with DEBUG log in my code, it seems django executes two queries no matter if the foreign key is accessed or not. Can someone explain this behaviour ?
class Person(models.Model):
# ...
name = models.CharField(max_length=250)
class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
As per doc
# Without select_related()...
b = Book.objects.get(id=4) # Executes a query.
p = b.author #Executes a query.
But with the get() it executes two queries
b = Book.objects.get(id=4) # Executes two queries (one for books one for author).
First of all, you need to call select select_related:
ids = [1,2,3,4]
query = Book.objects.filter(id__in=ids).select_related('author')
notice that I did that using the filter method and not the get method.
the reason is that select/prefetch related doesn't work with the get method.
if you still want only one object with select related you should do:
book = Book.objects.filter(id=4).select_related('author')[0]
author = book.author
or do:
book = Book.objects.select_related('author').get(id=4)
author = book.author
if you want to do it for multiple objects and get all the authors:
ids = [1,2,3,4]
query = Book.objects.filter(id__in=ids).select_related('author')
authors_in_query = [book.author for book in query]
I got a ValueError while trying to add model instances with a many-to-many relationship.
ValueError: "(Idea: hey)" needs to have a value for field "id" before this many-to-many relationship can be used.
A lot of responses were given here, but none was helpful.My (idea) solution was to "manually" input the "id" values.
>>> import django
>>> django.setup()
>>> from myapp1.models import Category, Idea
# Notice that I manually add an "id"
>>> id2=Idea.objects.create(
... title_en='tre',
... subtitle_en='ca',
... description_en='mata',
... id=5,
... is_original=True,
... )
>>> id2.save()
>>> cat22=Category(title_en='yo')
>>> cat22.save()
>>> id2.categories.add(cat22)
>>> Idea.objects.all()
<QuerySet [<Idea: tre>]>
>>> exit()
How do i command django to auto-add the "id" field?
Note: I tried adding autoField but failed, thanks
#python_2_unicode_compatible
class Idea(UrlMixin, CreationModificationDateMixin, MetaTagsMixin):
id = models.IntegerField(primary_key=True,)
title = MultilingualCharField(_("Title"), max_length=200,)
subtitle = MultilingualCharField(_("Subtitle"), max_length=200, blank=True,)
description = MultilingualTextField(_("Description"), blank=True,)
is_original = models.BooleanField(_("Original"), default=False,)
categories = models.ManyToManyField(Category,
You're confusing two things here:
With many-to-many relationships, when connecting two objects, both objects must already be saved to the database (have a primary key), because under the hoods, Django creates a third object that points at the two objects to connect them. It can only do that if both have an id, assuming id is the primary key.
When creating an object, you don't have to explicitly set the id (actually you shouldn't). By default, a django Model will have id set as an auto field and as a primary key (you can override that by specifying your own pk, but in general there's no need to). The id is automatically created when the model is saved the first time.
You saw the error because probably one of the objects (idea or category) wasn't saved to the database before you connected them. In your code sample, you don't have to pass id=5, it will work without it, because you save id2 and category before connecting them.
There are some constraints to this problem. We currently use a production database, with live Virtual Machine Statistics. We are trying to create a django interface, that interfaces with the tables we want our administrators to be able to edit. Thus, migrations are out of the question, for unless I have come to understand migrations wrong it will affect the current database structure and or data.
I matched the database structure exactly in my models.py file. However I have run into a few issues. One of the issues I have run into is when I try to add a new item under the admin control panel it will give me an integrity error as it is attempting to insert a null value for the field I have set as the primary key in the models.py file.
We are currently using an oracle database.
My Models.py not all of it but a sample of it.
class License(models.Model):
license_id = models.AutoField(primary_key = True, editable = False, db_column='license_id')
license_authority_id = models.ForeignKey(License_authoritie, on_delete = models.PROTECT, db_column='license_authority_id')
product = models.CharField(max_length = 20)
class Meta:
managed = False
db_table = 'licenses'
ordering = ['product']
def __unicode__(self): # Python 3: def __str__(self):
return self.product
class Vm_license(models.Model):
vm_license_id = models.AutoField(primary_key = True, db_column='vm_license_id')
vm_id = models.ForeignKey(Vm, on_delete = models.PROTECT, db_column='vm_id')
license = models.ManyToManyField(License)
class Meta:
managed = False
db_table = 'vm_licenses'
The error I get:
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/portal/vm_license/add/
Django Version: 1.6.5
Exception Type: IntegrityError
Exception Value:
ORA-01400: cannot insert NULL into ("DEV"."VM_LICENSES"."VM_LICENSE_ID")
On top of that I have run into another problem.
For these two tables, under the vm_licenses section in the admin panel which is a table that holds all VM's and their assigned licenses. I need the ability to select multiple licenses at a time for each vm_id under the add section of the admin panel but i'm not quite sure how to do this.
admin.py code
class vm_license_admin(admin.ModelAdmin):
#list_display = ('vm_id', 'license_id')
list_display = ('vm_id',)
search_fields = ('vm_id__vm_name',)
ordering = ('vm_id',)
filter_horizontal = ('license',)
admin.site.register(Vm_license, vm_license_admin)
I also made an oracle trigger to auto increment a primary key if there is none, but im still getting the same error.
CREATE OR REPLACE TRIGGER license_trigger
BEFORE INSERT ON vm_licenses
FOR EACH ROW
BEGIN
SELECT vm_license_seq.nextval
INTO :new.vm_license_id
FROM dual;
END;
to be more percise I am using a manytomany field and it displays correctly when I goto add a new item before clicking save and getting the null error, however if I goto an existing item it will say table or view doesnt exist.
I was going to comment on your question, but I do not have the reputation yet...
but can I suggest you post your relevant admin.py code? Perhaps there is something within it relating to the Null PK error.
With regards to the second part, a ManyToManyField sounds more suitable.
Thanks for taking time to read my question.
I have a django app with the following model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
...
class Visit(models.Model):
profile = models.ForeignKey(UserProfile)
date = models.DateField(auto_now_add=True, db_index=True)
ip = models.IPAddressField()
class Meta:
unique_together = ('profile', 'date', 'ip')
In a view:
profile = get_object_or_404(Profile, pk = ...)
get, create = Visit.objects.get_or_create(profile=profile, date=now.date(), ip=request.META['REMOTE_ADDR'])
if create: DO SOMETHING
Everything works fine, except that the Postgres Logs are full with duplicate key errors:
2012-02-15 14:13:44 CET ERROR: duplicate key value violates unique constraint "table_visit_profile_id_key"
2012-02-15 14:13:44 CET STATEMENT: INSERT INTO "table_visit" ("profile_id", "date", "ip") VALUES (1111, E'2012-02-15', E'xx.xx.xxx.xxx') RETURNING "table_visit"."id"
Tried different solution e.g.
from django.db import transaction
from django.db import IntegrityError
#transaction.commit_on_success
def my_get_or_create(prof, ip):
try:
object = Visit.objects.create(profile=prof, date=datetime.now().date(), ip=ip)
except IntegrityError:
transaction.commit()
object = Visit.objects.get(profile=prof, date=datetime.now().date(), ip=ip)
return object
....
created = my_get_or_create(prof, request.META['REMOTE_ADDR'])
if created: DO SOMETHING
This only helps for MySQL? Does anyone know how to avaid the duplicate key value errors for postgres?
Another possible reason for these errors in get_or_create() is data type mismatch in one of the search fields - for example passing False instead of None into a nullable field. The .get() inside .get_or_create() will not find it and Django will continue with new row creation - which will fail due to PostgreSQL constraints.
I had issues with get_or_create when using postgres. In the end I abandoned the boilerplate code for traditional:
try:
jobInvite = Invite.objects.get(sender=employer.user, job=job)
except Invite.DoesNotExist:
jobInvite = Invite(sender=employer.user, job=job)
jobInvite.save()
# end try
Have you at some point had unique=True set on Visit's profile field?
It looks like there's been a unique constraint generated for postgres that's still in effect. "table_visit_profile_id_key" is what it's auto generated name would be, and naturally it would cause those errors if you're recording multiple visits for a user.
If this is the case, are you using South to manage your database changes? If you aren't, grab it!
PostgreSQL behaves somewhat differently in some subtle queries, which results in IntegrityError errors, especially after you switch to Django 1.6. Here's the solution - you need to add select_on_save option to each failing model:
class MyModel(models.Model):
...
class Meta:
select_on_save = True
It's documented here: Options.select_on_save
I'm trying to write an internal API in my application without necessarily coupling it with the database.
class Product(models.Model):
name=models.CharField(max_length=4000)
price=models.IntegerField(default=-1)
currency=models.CharField(max_length=3, default='INR')
class Image(models.Model):
# NOTE -- Have changed the table name to products_images
width=models.IntegerField(default=-1)
height=models.IntegerField(default=-1)
url=models.URLField(max_length=1000, verify_exists=False)
product=models.ForeignKey(Product)
def create_product:
p=Product()
i=Image(height=100, widght=100, url='http://something/something')
p.image_set.add(i)
return p
Now, when I call create_product() Django throws up an error:
IntegrityError: products_images.product_id may not be NULL
However, if I call p.save() & i.save() before calling p.image_set.add(i) it works. Is there any way that I can add objects to a related object set without saving both to the DB first?
def create_product():
product_obj = Product.objects.create(name='Foobar')
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something', product=product_obj)
return product_obj
Explanation:
Product object has to be created first and then assign it to the Image object because id and name here is required field.
I am wondering why wouldn't you not require to make a product entry in DB in first case? If there is any specific reason then i may suggest you some work around?
EDIT: Okay! i think i got you, you don't want to assign a product to an image object initially. How about creating a product field as null is equal to true.
product = models.ForeignKey(Product, null=True)
Now, your function becomes something like this:
def create_product():
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something')
return image_obj
Hope it helps you?
I got same issue with #Saurabh Nanda
I am using Django 1.4.2. When I read in django, i see that
# file django/db/models/fields/related.py
def get_query_set(self):
try:
return self.instance._prefetched_objects_cache[rel_field.related_query_name()]
except (AttributeError, KeyError):
db = self._db or router.db_for_read(self.model, instance=self.instance)
return super(RelatedManager,self).get_query_set().using(db).filter(**self.core_filters)
# file django/db/models/query.py
qs = getattr(obj, attname).all()
qs._result_cache = vals
# We don't want the individual qs doing prefetch_related now, since we
# have merged this into the current work.
qs._prefetch_done = True
obj._prefetched_objects_cache[cache_name] = qs
That 's make sese, we only need to set property _prefetched_objects_cache for the object.
p = Product()
image_cached = []
for i in xrange(100):
image=Image(height=100, widght=100, url='http://something/something')
image_cached.append(image)
qs = p.images.all()
qs._result_cache = image_cached
qs._prefetch_done = True
p._prefetched_objects_cache = {'images': qs}
Your problem is that the id isn't set by django, but by the database (it's represented in the database by an auto-incremented field), so until it's saved there's no id. More about this in the documentation.
I can think of three possible solutions:
Set a different field of your Image model as the primary key (documented here).
Set a different field of your Production model as the foreign key (documented here).
Use django's database transactions API (documented here).