Problem:
I am having a website which has various apps (Accounts, Community, Company, Payments, Main_project_app).
What I want is to set up a google type similar architecture where the userbase is on one server and other apps such as Gmail, drive, etc. serve the specific services on different servers based on the user authentication from the main Google userbase server i.e. Accounts app which have information about the user such as username, email, password, etc must remain on one server and using the REST API's or something we connect accounts app with another server to take the user from sign-in page to dashboard and serve them other services by using all other apps.
I am a newbie so please if I have done some mistakes to explain or something do correct me.
TLDR: django can not do cross database foreign key lookups, which is hard to solve as a beginner, therefore django may not be the right tool, or the approach of splitting the database has to be omitted.
As you probably can imagine your question goes very far in terms of creating a distributed system and as these systems have a million options of how you want them to function and how they are interconnected with a database cluster.
But I can try to give you an overview and the first problem you will be likely confronted with.
where the userbase is on one server and other apps such as Gmail, drive, etc. serve the specific services on different servers
Okay, from what I understand you want to split your database by model type. Specifically you want the User model to be on a different database server with a different django instance doing authentication. This can basically be done by splitting the database locally.
Let us call the server with the User database the AuthServer and another server the AppServer. The AuthServer must serve an API that receives user credentials and decides whether the user can log in or not. Meanwhile the AppServer needs to cache User instances locally so it must not reauthenticate the user on every request. That is easily done by implementing a custom AuthenticationBackend. Now the AppServer receives a login request, can pass it to the AuthServer and receive a user instance on success or not on login failure.
This works well even with relationships between User and other app models because AuthServer and AppServer can use the same database server. But this somehow beats the purpose of splitting them up as you want to do it in the first place. Therefore django offers the approch of having multiple databases (the documentation here even uses that exact example that I will copy the code from). In your settings file you can store multiple database connections.
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
Further you can now write a DatabaseRouter that will probably look something like this (incomplete example):
class AuthRouter:
route_app_labels = {'auth', 'contenttypes'}
def db_for_read(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'users'
return 'default'
def db_for_write(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'users'
return 'default'
For completeness you have to add the router to the settings
DATABASE_ROUTERS = ['path.to.AuthRouter', ]
Now the first problem you are going to have is if you want to connect the User model to any other model, which is extremely likely. Django does not allow to have cross database relationships in terms of foreign keys to models in other databases. You can circumvent the issue in a very complicated manner by using IntegerField instead of ForeignKey fields and care for the datasbase integrety for your self but I will no recommend you to do it.
After all django is not the right tool to do such widespread services if you are on a beginner level as you say, or you have to prepare for very complicated and bug and error prone xperience :)
Related
I have two databases.
default has several models and works fine by itself.
When I add another database, it tries to creates tables from default database in the new database also. the new database does not have any models. All I am using it is to make a direct SQL query.
DATABASES = {
'default': {
'ENGINE':
..
..
},
'payments': {
'ENGINE':
..
..
},
}
Django does allow for multiple databases. You have to explicitly route to them for specific models and such though. In your case, it sounds like you will want to route all of your django models to the default DB and define specific cases for when to query the payments DB. I have linked a page, and specifically one section of that page for you to reference. I could copy and paste pieces of that page, but I think you'll find it more useful to just follow that page's explanation.
You should find this page helpful: MultiDB
This is particular will answer your question on managing two databases together: managers
Our current set up has Django hosted on Google's appengine with a MySQL database on Google's Cloud SQL.
The users (clients) are typically small businesses who we give a subdomain to for a multi-tenant database structure (1 database for each client).
As for determining which request should hit up which database, there is an existing middleware which strips the request path to get the subdomain and thus returning the correlated database alias defined in settings.py
from django.conf import settings
import threading
currentCompanyConnection = threading.local()
class DatabaseMiddleware(object):
def process_request(self, request):
url = request.build_absolute_uri()
if url.find("http://") < 0:
company = url.split("https://")[1].split(".")[0]
else:
company = url.split("http://")[1].split(".")[0]
global currentCompanyConnection
if company in settings.DATABASES:
currentCompanyConnection.company = company
else:
currentCompanyConnection.company = 'default'
request.currentCompany = str(currentCompanyConnection.company)
return None
class DBRouter(object):
def read_and_write(self, model, **hints):
return currentCompanyConnection.company
db_for_read = read_and_write
db_for_write = read_and_write
However, to allow our web application the functionality of a freemium self-service, a new database must be generated on the fly and imported into Django's settings.py dynamically for each user who sign up.
The last part is something I can't seem to figure out, since each time I change the settings.py, I must deploy it to appengine again. Aside from that, I'm not sure how to create a new database with pre-defined tables in Google's Cloud SQL from our web application.
Thanks for your help! I love resolving challenges from work, but this is something I simply haven't done before =O
You can't modify your source files once deployed. You can modify stuff in the blobstore or datastore.
I'd recommend storing the settings as structured data in the datastore, then have your settings.py read the data from the datastore and store them as python objects in settings.py that are accessible from other code. This should allow you to configure django to connect to multiple databases.
I'm not too familiar with CloudSQL, but I think you may still have a challenge of starting multiple CloudSQL instances and routing the appropriate traffic to the appropriate instances.
I'm taking EdX classes that use Ruby on Rails and python. That has given me courage to try and install and learn Django using Apach, mod_wsgi, and PostgreSQL. Following the detailed installation instructions, I first installed Apache, then mod_wsgi, and then PostgreSQL. I installed each from source and went through a little bit of tutorial with each and made sure they were properly installed. I've got a postgres user setup to run the PostgreSQL server and I was able to create a user "role" for myself as well as an admin role that my role inherits from that can create a database etc. I tried out some SQL in psql following a tutorial to make tables etc. I know how to grant privileges for a given role.
So anyway, I'm pretty close to the step where I would actually install Django from source, but I'm not sure how to follow this advice from the installation instructions:
If you plan to use Django's manage.py syncdb command to automatically create database tables for your models, you'll need to ensure that Django has permission to create and alter tables in the database you're using; if you plan to manually create the tables, you can simply grant Django SELECT, INSERT, UPDATE and DELETE permissions.
Maybe after I follow the steps to actually install Django and go through some tutorials, I'll understand exactly what needs to be setup in PostgreSQL to grant Django those permissions, but if I follow the installation instructions in order, it would seem to be saying I should setup those permissions now before installing Django. If I can get someone to tell me how to do it here before I do the install of Django, I'd appreciate it.
In the settings.py file of a django project, there is a snippet that says something like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproj_db',
'USER': 'myproj_user',
'PASSWORD': '123',
'HOST': '',
'PORT': '',
},
}
What this does is that it tells Django what user (postgres user in this case) and database is used in conjunction with your django project.
Normally, you will need to create this myproj_user together with the myproj_db.
When you create this user, you can choose to give it permissions like so:
CREATE USER myproj_user WITH PASSWORD '123' SUPERUSER CREATEDB CREATEROLE LOGIN;
This creates the myproj_user with superuser, createdbm createrole, login permissions allowed to the user.
And then the database like so:
CREATE DATABASE myproj_db WITH OWNER myproj_user TEMPLATE=template1 ENCODING='utf-8';
You say that you know how to grant privileges for a given role, which is what you need to do for Django before installing it (running syncdb).
This is part of setting up your database for use with Django – a step you take before creating each Django project. Each Django project corresponds to a site that you build with Django and is completely separate from another Django project. To each project belongs a database.* You can install Django the framework before you setup your database, because Django the framework doesn't do anything on its own.
Either you give Django permissions to create tables for you, in which case it can create tables for you (using manage.py syncdb). Or, you use manage.py sqlall <app> to get SQL that you run yourself to create the tables needed (which might be nice if you're paranoid about security).
To grant all permissions to a user for a specific database (option 1) in Postgres, use the command (from psql):
GRANT ALL PRIVILEGES ON DATABASE <db-name> TO <username>;
* Technically, they can share a database by simply configuring them to use the same one.
Django uses ORM (Object Relational Mapper). What that means is that you do not directly deal with database tables for querying things however you deal with certain classes which then deal with the database. This is very useful and much more user-friendly then doing manually SQL. Consider the following example:
SELECT * FROM foo WHERE field="value"
vs
Foo.objects.filter(field='value')
In ORM, you describe the tables you have by making certain classes (in Django we call them models) and those models correspond to tables in the db, and their fields correspond to the columns in the db table. The following are very similar:
CREATE TABLE foo (
title varchar(50),
description text,
likes integer
);
and
class Foo(models.Model):
title = models.CharField(max_length=50)
description = models.TextField()
likes = models.IntegerField()
However it is waste of time for you as a developer to construct the SQL statements for creating tables, and describing those tables in Python as models. Not to do that, Django allows you once you define all your models, to create db tables for you. That is the purpose of the syncdb command. It takes all the installed apps and models within them and creates tables within your database for them. However as you already mentioned in your question, databases have roles and those roles have permissions. For example, one of the permissions is CREATE as described here. So what Django documentation is saying is for you to grand all necessary permission to a role which Django will use to interact with the db for Django to be able to create necessary db tables it needs as well as possibly modify them later.
I'm planning to deploy a Django site using Apache + mod_wsgi and PostgreSQL on Ubuntu 10.04.
I intend to connect to the database using IDENT authentication. For this I need to create a Postgresql user for Apache (www-data). I have chosen not to make this a superuser or provide any special privileges.
I have then created a database. I actually did this twice during testing. The first time I set the Apache user as the owner; the second time I set the owner as myself (superuser), and granted all privileges on the database to the Apache user.
When I use the Django syncdb management command (as myself), the tables created are not accessible to the Apache user. This can be resolved by granting all permissions to the Apache user for each table, but that's a bit of a nuisance.
The alternative seems to be allowing access as a superuser.
Is it considered safe/acceptable for my project to access a local db as a Postgresql superuser, and is it safe to use IDENT authentication? If not, what is the common practice?
EDIT: I've since found that switching PostgreSQL to use md5 authentication for local connections makes life easier.
When using ident authentication, connections to the database are via the Apache user during normal operation. When Django management commands are used, the connections are via the current user.
If you use MD5, both situations will connect to the database using the details specified in the DATABASES section of your settings.py file, avoiding the problems listed above.
I'm still interested to know if using a PostgreSQL superuser is wise.
Having applications connect as a superuser is almost definitely unwise. Unless the application needs to actually create and/or drop databases itself (and this is extremely unlikely), I don't think it's ever necessary. If the application connects to a database as that database's owner, it is effectively a superuser within the confines of that database, which might not be too bad.
I generally have applications access the database using an account authenticating with MD5. It's possible, for example, to set up pg_hba.conf such that the application account is the only account that can use MD5 authentication, and all other users on the local machine use ident/peer authentication.
It sounds like what you actually needed here was a role to group the Apache user and the other Django users together, so you could grant them access en masse.
Postgresql does have ways to grant permissions for all tables etc in a schema at once, and also a way to specify default permissions to be applied to new objects. This previous answer may be helpful: How do you create a read-only user in PostgreSQL?
IDENT authentication ended up being more hassle than it was worth. Here's what I ended up doing to avoid the use of a PostgreSQL superuser role...
Switch to the postgres linux user:
sudo su - postgres
Edit the PostgreSQL host-based authentication configuration file:
nano /etc/postgresql/8.4/main/pg_hba.conf
Scroll to near the bottom of this file, looking for the line which looks like this:
local all all ident
Change ident to md5, exit and save. This tells PostgreSQL to use an MD5-encrypted password for authentication on local connections. Now restart PostgreSQL:
/etc/init.d/postgresql-8.4 restart
Create a PostgreSQL user:
createuser django_user --pwprompt
Don't accept any of the special privileges when prompted. Now create a new database:
createdb -E UTF8 -O django_user django_db
Those options encode the database in UTF8 and set the owner to django_user. You can now exit back to the original linux user account:
exit
Your project settings file (settings.py) will need to include something like this:
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': 'django_db',
'USER': 'django_user',
'PASSWORD': '[your password]',
'HOST': '',
'PORT': '',
}
}
When you run python manage.py syncdb or any other Django management commands, the settings above will be used to authenticate with the database.
I am using mysql database. I have many schemas with many tables. I want to create a Django admin interface for various tables in different schemas. Currently for a single schema I am using a setting like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'schema1',
'USER': 'test',
'PASSWORD': 'test',
'HOST': 'example.abc.com',
'PORT': '',
}
After creating an app, admin interface is created for whatever models I register in the admin.py of that app for this schema. Now I want to create an other app where I register models of another schema in its admin.py. These models will belong to different schema. Now how do I create an admin interface for the second app that points to different schema?
Is it possible to create two Django projects with two different settings.py and two different admin interfaces? (So that each will point to different schema.)
I have googled a lot about this. But couldn't find any info. May be there is a simple way and I am approaching this in a wrong way. Any help is appreciated.
Thanks in advance.
This is documented well on the django doc here http://docs.djangoproject.com/en/dev/topics/db/multi-db/#exposing-multiple-databases-in-django-s-admin-interface
I'm not quite sure if you mean that you want to handle different databases or just have different models registered. If you want to have different models in different admin sites, you can register multiple admin sites with different models. You can then access one site eg with '/admin' the other with '/otheradmin'. Maybe you find django-admin-tools useful to customize the display of your models/apps within the admin!