Upon initialization of an application in django, before the postgresql database has been created, if one tries to get/all/create an object they will usually see an error like this:
>>> Model.objects.all()
{StackTrace}
...
DatabaseError: relation "model" does not exist
...
In my application I would like to be able to test for the models existence and run code if it exists, is this possible using django?
Pseudocode:
if not (table_exists(model))
return
my_models = Model.objects.all()
...
You could catch the exception that's thrown while trying to access the model:
from django.db import DatabaseError
try:
Model.objects.exists()
except DatabaseError:
print 'DB not yet initialized'
else:
print 'DB table exists'
I don't know why you would want to do this, but could you just issue raw sql to see if the table exists??
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';
Related
How can I check, which query django 1.4.11 generates for this query:
obj = Model.objects.get(code='code')
I've tried:
print Model.objects.get(code='code').query
but there is such method for model object.
How can I get raw sql?
It doesn't work because query is a property of the Queryset object and when you do a .get() the Queryset it´s evaluated (and became an instance of Model)
If you try:
>>> type(Model.objects.get(code='code'))
<class 'app.models.Model'>
>>> print Model.objects.get(code='code').query
AttributeError: 'Model' object has no attribute 'query'
But instead with:
>>> type(Model.objects.all())
<class 'django.db.models.query.QuerySet'>
>>> print Model.objects.all().query
SELECT "model.Model" from ...
Now, to get the SQL of all the queries you have several options:
If DEBUG=True you can use this:
from django.db import connection
print connection.queries
Use django-debug-toolbar
Use the built-in django logging https://docs.djangoproject.com/en/dev/topics/logging/#django-db-backends You can find examples on how to setup here in SO.
I have noticed with model objects in django that I can do:
MyModel.objects.all()
I can do this without making a new MyModel object. How/why does this work?
Edited the question:
I am not asking about the base Model class, but I am talking about a model called MyModel that extends from the base Model class
What is Model.objects
Model classes, have a Manager class, you can get it like this:
YourModel.objects
The Manager is the one making SQL queries, for example, this will return a QuerySet:
YourModel.objects.all()
A QuerySet behaves mostly like a normal python list, except that it will make an SQL query when it is first evaluated.
The base model class has no manager !
So you cannot do Model.objects.all() as you said:
In [1]: from django.db.models import Model
In [2]: Model.objects
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/home/jpic/env/local/lib/python2.7/site-packages/django/core/management/commands/shell.pyc in <module>()
----> 1 Model.objects
AttributeError: type object 'Model' has no attribute 'objects'
A QuerySet will fail if the table doesn't exist !
It will throw a DatabaseError:
50 def execute(self, query, args=None):
51 try:
---> 52 return self.cursor.execute(query, args)
53 except Database.IntegrityError, e:
54 raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
DatabaseError: relation "formapp_testmodel" does not exist
LINE 1: SELECT "formapp_testmodel"."id" FROM "formapp_testmodel" LIM...
If you define a model class MyModel(models.Model) and then run python manage.py syncdb, django will create a databse table yourapp_mymodel. You can run queries against that db table that return no tuples, so you can create a queryset that contains no results - eg MyModel.objects.none().
The queryset api would be pretty hopeless if it threw an exception every time you ran a query that returned no results.
(I've assumed that you meant to ask why you can do MyModel.objects.all() rather than Model.objects.all(). As jpic shows in his answer you can't do Model.objects.all().
I am using PostgreSQL as my backend with psycopg2, and I'm noticing something that seems strange to me. My set up is as follows:
class ParentModel(models.Model):
field1 = models.IntegerField()
class ChildModel(ParentModel):
field2 = models.IntegerField(unique=True)
When I try to save a ChildModel object with a duplicate field2 from the shell, I get an InternalError instead of an IntegrityError, but only if the save is done in a transaction.commit_on_success block like so:
with transaction.commit_on_success():
newObj = ChildModel(field1=5, field2=10) # Assume that a ChildModel with field2 == 10 exists
Outside of the transaction block, I get an IntegrityError, as I would expect.
When running inside view code, I always get the InternalError following the failed update even using savepoints:
try:
sid = transaction.savepoint()
newObj = ChildModel(field1=5, field2=10) # Assume that a ChildModel with field2 == 10 exists
transaction.savepoint_commit(sid)
except IntegrityError:
transaction.savepoint_rollback(sid)
except InternalError:
transaction.savepoint_rollback(sid)
... other stuff ... # raises InternalError on next database hit
Same thing happens if I do it within a commit_on_success transaction with block, instead of using savepoints. I have TransactionMiddleware installed and I am not running PostgreSQL in autocommit mode.
I can avoid the situation by simply checking to make sure the duplicate object doesn't exist, but I want to udnerstand what's going wrong with my understanding of Django transactions. What gives?
I had the exact same problem. I looked at Postgres log files and could see that another query is being run after the query which caused the integrity error, which leads to actual error being eclipsed in default transaction settings. I found out that the extra query is coming from django-debug-toolbar app.
Google pointed me to https://github.com/django-debug-toolbar/django-debug-toolbar/issues/351 which seems to solve the problem.
In short, if you are using django-debug-toolbar<=0.9.4, disabling it solves the problem.
How do you view the SQL generated by Django for a DELETE?
When doing a SELECT operation on a query set, you can do this:
>>> qs = Entry.objects.filter(date__gt='2010-06-01')
>>> qs.query.as_sql()
('SELECT ...)
But I don't know how to get the SQL for what happens when I do qs.delete().
It looks a bit more involved because Django "emulates the behavior of the SQL constraint ON DELETE CASCADE" when deleting objects.
(Background: trying to debug an IntegrityError generated by a foreign key constraint when deleting a subclassed model object.)
This works well enough:
>>> from django.db import connection
>>> connection.queries[:-10]
Thought the exceptions occurred before the queries were added to connection.queries, but they are indeed present.
Here's another method which relies on Django internals and doesn't include queries to do cascading deletes, but doesn't require executing the query:
from django.db.models import sql
qs = Entry.objects.filter(date__gt='2010-06-01')
query = qs.query.clone()
query.__class__ = sql.DeleteQuery
print(query)
You could try running django-debug-toolbar and see the queries that way.
class dbview(models.Model):
# field definitions omitted for brevity
class Meta:
db_table = 'read_only_view'
def main(request):
result = dbview.objects.all()
Caught an exception while rendering: (1054, "Unknown column 'read_only_view.id' in 'field list'")
There is no primary key I can see in the view. Is there a workaround?
Comment:
I have no control over the view I am accessing with Django. MySQL browser shows columns there but no primary key.
When you say 'I have no control over the view I am accessing with Django. MySQL browser shows columns there but no primary key.'
I assume you mean that this is a legacy table and you are not allowed to add or change columns?
If so and there really isn't a primary key (even a string or non-int column*) then the table hasn't been set up very well and performance might well stink.
It doesn't matter to you though. All you need is a column that is guaranteed to be unique for every row. Set that to be 'primary_key = True in your model and Django will be happy.
There is one other possibility that would be problemmatic. If there is no column that is guaranteed to be unique then the table might be using composite primary keys. That is - it is specifying that two columns taken together will provide a unique primary key. This is perfectly valid relational modelling but unfortunatly unsupported by Django. In that case you can't do much besides raw SQL unless you can get another column added.
I have this issue all the time. I have a view that I can't or don't want to change, but I want to have a page to display composite information (maybe in the admin section). I just override the save and raise a NotImplementedError:
def save(self, **kwargs):
raise NotImplementedError()
(although this is probably not needed in most cases, but it makes me feel a bit better)
I also set managed to False in the Meta class.
class Meta:
managed = False
Then I just pick any field and tag it as the primary key. It doesn't matter if it's really unique with you are just doing filters for displaying information on a page, etc.
Seems to work fine for me. Please commment if there are any problems with this technique that I'm overlooking.
If there really is no primary key in the view, then there is no workaround.
Django requires each model to have exactly one field primary_key=True.
There should have been an auto-generated id field when you ran syncdb (if there is no primary key defined in your model, then Django will insert an AutoField for you).
This error means that Django is asking your database for the id field, but none exists. Can you run django manage.py dbshell and then DESCRIBE read_only_view; and post the result? This will show all of the columns that are in the database.
Alternatively, can you include the model definition you excluded? (and confirm that you haven't altered the model definition since you ran syncdb?)
I know this post is over a decade old, but I ran into this recently and came to SO looking for a good answer. I had to come up with a solution that addresses the OP's original question, and, additionally, allows for us to add new objects to the model for unit testing purposes, which is a problem I still had with all of the provided solutions.
main.py
from django.db import models
def in_unit_test_mode():
"""some code to detect if you're running unit tests with a temp SQLite DB, like..."""
import sys
return "test" in sys.argv
"""You wouldn't want to actually implement it with the import inside here. We have a setting in our django.conf.settings that tests to see if we're running unit tests when the project starts."""
class AbstractReadOnlyModel(models.Model):
class Meta(object):
abstract = True
managed = in_unit_test_mode()
"""This is just to help you fail fast in case a new developer, or future you, doesn't realize this is a database view and not an actual table and tries to update it."""
def save(self, *args, **kwargs):
if not in_unit_test_mode():
raise NotImplementedError(
"This is a read only model. We shouldn't be writing "
"to the {0} table.".format(self.__class__.__name__)
)
else:
super(AbstractReadOnlyModel, self).save(*args, **kwargs)
class DbViewBaseModel(AbstractReadOnlyModel):
not_actually_unique_field = IntegerField(primary_key=True)
# the rest of your field definitions
class Meta:
db_table = 'read_only_view'
if in_unit_test_mode():
class DbView(DbViewBaseModel):
not_actually_unique_field = IntegerField()
"""This line removes the primary key property from the 'not_actually_unique_field' when running unit tests, so Django will create an AutoField named 'id' on the table it creates in the temp DB that it creates for running unit tests."""
else:
class DbView(DbViewBaseModel):
pass
class MainClass(object):
#staticmethod
def main_method(request):
return DbView.objects.all()
test.py
from django.test import TestCase
from main import DbView
from main import MainClass
class TestMain(TestCase):
#classmethod
def setUpTestData(cls):
cls.object_in_view = DbView.objects.create(
"""Enter fields here to create test data you expect to be returned from your method."""
)
def testMain(self):
objects_from_view = MainClass.main_method()
returned_ids = [object.id for object in objects_from_view]
self.assertIn(self.object_in_view.id, returned_ids)