Multiple Databases in Django 1.0.2 with custom manager - django

I asked this in the users group with no response so i thought I would try here.
I am trying to setup a custom manager to connect to another database
on the same server as my default mysql connection. I have tried
following the examples here and here but have had no luck. I get an empty tuple when returning
MyCustomModel.objects.all().
Here is what I have in manager.py
from django.db import models
from django.db.backends.mysql.base import DatabaseWrapper
from django.conf import settings
class CustomManager(models.Manager):
"""
This Manager lets you set the DATABASE_NAME on a per-model basis.
"""
def __init__(self, database_name, *args, **kwargs):
models.Manager.__init__(self, *args, **kwargs)
self.database_name = database_name
def get_query_set(self):
qs = models.Manager.get_query_set(self)
qs.query.connection = self.get_db_wrapper()
return qs
def get_db_wrapper(self):
# Monkeypatch the settings file. This is not thread-safe!
old_db_name = settings.DATABASE_NAME
settings.DATABASE_NAME = self.database_name
wrapper = DatabaseWrapper()
wrapper._cursor(settings)
settings.DATABASE_NAME = old_db_name
return wrapper
and here is what I have in models.py:
from django.db import models
from myproject.myapp.manager import CustomManager
class MyCustomModel(models.Model):
field1 = models.CharField(max_length=765)
attribute = models.CharField(max_length=765)
objects = CustomManager('custom_database_name')
class Meta:
abstract = True
But if I run MyCustomModel.objects.all() I get an empty list.
I am pretty new at this stuff so I am not sure if this works with
1.0.2, I am going to look into the Manager code to see if I can figure
it out but I am just wondering if I am doing something wrong here.
UPDATE:
This now in Django trunk and will be part of the 1.2 release
http://docs.djangoproject.com/en/dev/topics/db/multi-db/

You may want to speak to Alex Gaynor as he is adding MultiDB support and its pegged for possible release in Django 1.2. I'm sure he would appreciate feedback and input from those that are going to be using MultiDB. There is discussions about it in the django-developers mainling list. His MultiDB branch may even be useable, I'm not sure.
Since I guess you probably can't wait and if the MultiDB branch isn't usable, here are your options.
Follow Eric Flows method, bearing in mind that its not supported and new released of Django may break it. Also, some comments suggest its already been broken. This is going to be hacky.
Your other option would be to use a totally different database access method for one of your databases. Perhaps SQLAlchemy for one and then Django ORM. I'm going by the guess that one is likely to be more Django centric and the other is a legacy database.
To summarise. I think hacking MultiDB into Django is probably the wrong way to go unless your prepared to keep up with maintaining your hacks later on. Therefore I think another ORM or database access would give you the cleanest route as then you are not going out with supported features and at the end of the day, its all just Python.

My company has had success using multiple databases by closely following this blog post: http://www.eflorenzano.com/blog/post/easy-multi-database-support-django/

This probably isnt the answer your looking for, but its probably best if you move everything you need into the one database.

Related

How can I use model (DB) data in djangoCMS, without creating views?

I think my question is pretty clear:
I am interested in finding out some ways of using model (DB) data without creating views.
I have already created a website using djangocms and now I would like to develop a little blog.
I am aware of this project but this is not as straight forward as I'd need ( it is pretty complex for my purpose ).
For example, in the first place, I would like to know if there's any possibility to create a basic template in which I will be able to display the users(their names) directly from the database.
In Django, you basically can't get data from a model to a template without a view that puts it there. That's the whole point of views.
You don't necessarily have to create a views.py file, because there might be other mechanisms that you can make use of, such as django CMS's plugin system.
I don't understand what connection your question has to developing a weblog though.
What you can do is create a context processor which essentially is a function which returns a dictionary & it's added to every request context.
For example, I have a model which provides information for the site as a whole, like social links, a name etc. Take a look;
from django.contrib.sites.models import get_current_site
from .models import SiteSettings
def site_settings(request):
current_site = get_current_site(request)
try:
settings = SiteSettings.objects.get(site_id=current_site.id)
except SiteSettings.MultipleObjectsReturned:
settings = SiteSettings.objects.filter(
site_id=current_site.id
).order_by('id')[0]
except SiteSettings.DoesNotExist:
return {
'SITE_SITE_NAME': None,
'SITE_SHORT_NAME': None,
}
# SiteSettings object exists, so assign the attributes you want, to context
# variables returned by the context processor for all views.
data = {
'SITE_SITE_NAME': settings.site_name,
'SITE_SHORT_NAME': settings.short_name,
}
return data
So by doing that you can use things like {{ SITE_SHORT_NAME }} in any template, and this obviously makes any information you hold in a table available via one of these processors. Just make sure you create necessary DoesNotExist exceptions when doing this with models.

django-registration - new user to a specific group

I'm using (and learning) Django on my newest product, and I just installed and started using django-registration app to allow my users to auto-register themselves.
Everything's working good, but I'm thinking about assigning every new user to a specific group (already created), so I can "separate" the admins from the normal users.
How can I do this? The documentation about this app is awful, IMO.
I didn't create any model or configuration to start using the django-registration app.
I ended up making a fork for this, as this was driving me crazy.
https://github.com/kcunning/django-registration
Basically, add the following to your settings.py file:
REGISTRATION_DEFAULT_GROUP_NAME="Groupname"
Any user added through the registration module will be given that group upon creation.
To start, I'm not an expert at d-r (django-registration) and actually only checked it out because you mentioned it in your question. So, if anyone else has a better answer please submit it. Anyway, here it goes.
I noticed that the way users (or "profiles" as d-r calls them) are created is found in the create_inactive_user function located in registration.models.RegistrationManager. So, in this function after:
new_user.is_active = False
new_user.save()
You could add something like:
default_group = Group.objects.get(name=WHATEVER_THE_GROUP_NAME_IS)
user_group, is_created = UserGroup.get_or_create(user=new_user)
user_group.group = default_group
#user_group.groups.add(default_group) # Django ver. 1.7+
user_group.save()
You could probably do without the get_or_create as I don't think you can create more than one user with the same username, but I would rather be safe than sorry.
This method will cause you to edit the d-r files (which may not be the best thing to do) along with hardcoding certain things (such as the default group name), but it should be a good stepping off point.
PS. the default group name could be a variable located in the settings.py file, which would make it more accessible and pretty. Example:
settings.py:
DEFAULT_GROUP_NAME = 'YOUR GROUP NAME'
registration/models.py:
default_group = Group.objects.get(name=settings.DEFAULT_GROUP_NAME)
Anyway, hope that helps.
django-registration-redux has custom signals that can be used to assign registered user to a specific group. "user_registered" signal can be used for this purpose. Please refer to django-registration-redux documentation
from django.contrib.auth.models import Group
def user_created(sender, user, request, **kwargs):
group = Group.objects.get(name='Kurum')
group.user_set.add(user)
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)

Can you find out if a Django Model instance is "dirty"?

I really like the feature of SQLAlchemy that allows you to see if an object is dirty: if it has been modified since it was retrieved from the database, or the last time it was saved.
Is it possible to find this information from the Django ORM?
Note this is not the same as Dirty fields in django, as I don't care about what the previous data was, although S.Lott's answer may provide a way to do it, but I would like a way that doesn't hit the database.
I have also looked at the django.db.transaction.is_dirty(), but this doesn't seem to be the solution.
A solution that does do a database query:
class DirtyMixin(object):
#property
def is_dirty(self):
db_obj = self.__class__.objects.get(self.pk)
for f in self._meta.local_fields:
if self.__getattribute__(f.name) != db_obj.__getattribute__(f.name):
return True
return False
You can then add this as an ancestor class to a model. Or, monkey-patch the forms.Model class, if you like.
from django.db import models
models.Model.__bases__ = (DirtyMixin,) + models.Model.__bases__
Another way, involving overriding __setattr__, is discussed at some length in this Django ticket.
try use lck.django class TimeTrackable

Django: Querying read-only view with no primary key

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)

How to lookup django session for a particular user?

I am writing an application where I will be accessing the database from django and from a stand alone application. Both need to do session verification and the session should be the same for both of them. Django has a built in authentication/session verification, which is what I am using, now I need to figure out how to reuse the same session for my stand alone application.
My question is how can I look up a session_key for a particular user?
From what it looks there is nothing that ties together auth_user and django_session
This answer is being posted five years after the original question, but this SO thread is one of the top Google results when searching for a solution to this problem (and it's still something that isn't supported out of the box with Django).
I've got an alternate solution for the use case where you're only concerned with logged in user sessions, which uses an additional UserSession model to map users to their sessions, something like this:
from django.conf import settings
from django.db import models
from django.contrib.sessions.models import Session
class UserSession(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
session = models.ForeignKey(Session)
Then you can simply save a new UserSession instance any time a user logs in:
from django.contrib.auth.signals import user_logged_in
def user_logged_in_handler(sender, request, user, **kwargs):
UserSession.objects.get_or_create(user = user, session_id = request.session.session_key)
user_logged_in.connect(user_logged_in_handler)
And finally when you'd like to list (and potentially clear) the sessions for a particular user:
from .models import UserSession
def delete_user_sessions(user):
user_sessions = UserSession.objects.filter(user = user)
for user_session in user_sessions:
user_session.session.delete()
That's the nuts and bolts of it, if you'd like more detail I have a blog post covering it.
I found this code snippet
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
session_key = '8cae76c505f15432b48c8292a7dd0e54'
session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)
print user.username, user.get_full_name(), user.email
here
http://scottbarnham.com/blog/2008/12/04/get-user-from-session-key-in-django/
Have not verified it yet, but looks pretty straight forward.
This is somewhat tricky to do, because not every session is necessarily associated with an authenticated user; Django's session framework supports anonymous sessions as well, and anyone who visits your site will have a session, regardless of whether they're logged in.
This is made trickier still by the fact that the session object itself is serialized -- since Django has no way of knowing which data exactly you want to store, it simply serializes the dictionary of session data into a string (using Python's standard "pickle" module) and stuffs that into your database.
If you have the session key (which will be sent by the user's browser as the cookie value "sessionid"), the easiest way to get at the data is simply to query the Session table for the session with that key, which returns a Session object. You can then call that object's "get_decoded()" method to get the dictionary of session data. If you're not using Django, you can look at the source code (django/contrib/sessions/models.py) to see how the session data is deserialized.
If you have the user id, however, you'll need to loop through all of the Session objects, deserializing each one and looking for one which has a key named "_auth_user_id", and for which the value of that key is the user id.
Modifying the django_session table to add an explicit user_id can make life a lot easier. Assuming you do that (or something similar), here are four approaches to munging things to your liking:
Fork the django.contrib.session code. I know, I know, that's a horrible thing to suggest. But it's only 500 lines including all backends and minus the tests. It's pretty straightforward to hack. This is the best route only if you are going to do some serious rearranging of things.
If you don't want to fork, you could try connecting to the Session.post_save signal and munge there.
Or you could MonkeyPatch contrib.session.models.Session.save(). Just wrap the existing method (or create a new one), breakout/synthesize whatever values you need, store them in your new fields, and then super(Session, self).save().
Yet another way of doing this is to put in 2 (yes, two) middleware classes -- one before and one after SessionMiddleware in your settings.py file. This is because of the way middleware is processed. The one listed after SessionMiddleware will get, on the inbound request, a request with the session already attached to it. The one listed before can do any processing on the response and/or change/resave the session.
We used a variation on this last technique to create pseudo-sessions for search engine spiders to give them special access to material that is normally member-only. We also detect inbound links where the REFERER field is from the associated search engine and we give the user full access to that one article.
Update:
My answer is now quite ancient, although it still is mostly correct. See #Gavin_Ballard's much more recent answer (9/29/2014) below for yet another approach to this problem.
Peter Rowell, thanks for your response. It was a tremendous help. This is what I did to get it working. Only had to change one file in djang.contrib.sessions.
In django/contrib/sessions/models.py, add the user_id to the table (add to DB table manually or drop table and run manage.py syncdb).
class Session(models.Model):
...
user_id = models.IntegerField(_('user_id'), null=True)
...
def save(self, *args, **kwargs):
user_id = self.get_decoded().get('_auth_user_id')
if ( user_id != None ):
self.user_id = user_id
# Call the "real" save() method.
super(Session, self).save(*args, **kwargs)
Now in your view where you do login (if you use django's base login, you will have to override it)
# On login, destroy all prev sessions
# This disallows multiple logins from different browsers
dbSessions = Session.objects.filter( user_id = request.user.id )
for index, dbSession in enumerate( dbSessions ):
if ( dbSession.session_key != request.session.session_key ):
dbSession.delete()
This worked for me.
I ran across this problem when I wanted to kick out a spammer. It seems setting their account to "inactive" isn't enough, because they can still come in on their previous session. So - how to delete a session for a specific user, or how to deliberately expire a session for a specific user?
The answer is to user the last_login field to track down the time at which the session was disabled, which tells you that the expire_date is two weeks later, which lets you perform a useful filter on the sessions table:
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
from datetime import datetime
from dateutil.relativedelta import relativedelta
baduser = User.objects.get(username="whoever")
two_weeks = relativedelta(weeks=2)
two_hours = relativedelta(hours=2)
expiry = baduser.last_login + two_weeks
sessions = Session.objects.filter(
expire_date__gt=expiry - two_hours,
expire_date__lt=expiry + two_hours
)
print sessions.count() # hopefully a manageable number
for s in sessions:
if s.get_decoded().get('_auth_user_id') == baduser.id:
print(s)
s.delete()