I am a newcomer to MongoDB and currently attempting to establish a connection between MongoDB and Django. However, I have been successful in establishing a connection with Postgres, but I am facing difficulties in connecting to MongoDB. Although there are no errors occurring during the migration process, I am not seeing any migration on the MongoDB side.
Setting.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'base_app.apps.BaseAppConfig',
'document_app.apps.DocumentAppConfig',
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': os.environ.get('POSTGRES_HOST'),
'NAME': os.environ.get('POSTGRES_NAME'),
'USER': os.environ.get('POSTGRES_USER'),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
'PORT': os.environ.get('POSTGRES_PORT'),
},
"mongodb": {
"ENGINE": "djongo",
"NAME": os.environ.get('MONGO_DB_NAME'),
"CLIENT": {
"host": os.environ.get('MONGO_DB_HOST'),
"port": int(os.environ.get('MONGO_DB_PORT')),
# "username": os.environ.get('MONGO_DB_USERNAME'),
# "password": os.environ.get('MONGO_DB_PASSWORD'),
},
'TEST': {
'MIRROR': 'default',
},
}
}
DATABASE_ROUTERS = ['base_app.utils.routers.MyRouter', 'document_app.utils.routers.MyRouter']
base_app/model.py
class PostgresModel(models.Model):
# fields for Postgres database here
title = models.CharField(max_length=100)
content = models.TextField()
class Meta:
db_table = 'postgres_model'
app_label = 'base_app'
managed = True
abstract = False
document_app\models.py
from djongo import models as djmodels
from django.db import models
class MongoDBModel(djmodels.Model):
# fields for MongoDB database here
name = models.CharField(max_length=100)
age = models.IntegerField()
address = models.CharField(max_length=200)
class Meta:
db_table = 'mongodb_model'
app_label = 'document_app'
managed = True
abstract = False
class MongoDBDocument(djmodels.Model):
# fields for MongoDB database here
name = models.CharField(max_length=100)
age = models.IntegerField()
address = models.CharField(max_length=200)
class Meta:
db_table = 'mongodb_model_document'
app_label = 'document_app'
managed = True
abstract = False
base_app.utils.routers.MyRouter
class MyRouter:
"""
A router to control all database operations on models in the
myapp application.
"""
def db_for_read(self, model, **hints):
"""
Attempts to read models go to the default PostgreSQL database.
"""
if model._meta.app_label == 'base_app':
return 'default'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write models go to the MongoDB database.
"""
if model._meta.app_label == 'base_app':
return 'default'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations only between objects that are both in the MongoDB database.
"""
if obj1._meta.app_label == 'base_app' and obj2._meta.app_label == 'base_app':
if obj1._state.db == 'default' and obj2._state.db == 'default':
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Ensure that all models are created in the default PostgreSQL database.
"""
if app_label == 'base_app':
return db == 'default'
return None
'document_app.utils.routers.MyRouter'
class MyRouter:
"""
A router to control all database operations on models in the
myapp application.
"""
def db_for_read(self, model, **hints):
"""
Attempts to read models go to the default PostgreSQL database.
"""
if model._meta.app_label == 'document_app':
return 'mongodb'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write models go to the MongoDB database.
"""
if model._meta.app_label == 'document_app':
return 'mongodb'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations only between objects that are both in the MongoDB database.
"""
if obj1._meta.app_label == 'document_app' and obj2._meta.app_label == 'document_app':
if obj1._state.db == 'mongodb' and obj2._state.db == 'mongodb':
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Ensure that all models are created in the default PostgreSQL database.
"""
if app_label == 'document_app':
return db == 'mongodb'
return None
Folder Structure
Requirement.txt
asgiref 3.6.0
Django 4.1.7
djangorestframework 3.14.0
pip 22.3.1
python-dotenv 0.21.1
pytz 2022.7.1
setuptools 65.5.0
sqlparse 0.4.3
tzdata 2022.7
I trust that the information provided is adequate. I am seeking your assistance in identifying the cause of the unsuccessful migration of MongoDB in Django. Your assistance would be greatly appreciated. Thank you in advance
Related
How can we access other database tables into django application database. Here 'default' database is main application and 'cl_db' is other application which is developed in asp.net but using same database server here how can we sync 'cl_db' database into 'default' database.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'fail_over',
'USER': 'SomeUser',
'PASSWORD': 'SomePassword',
'HOST': '127.0.0.1',
'PORT': '',
},
'cl_db': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'cl_dev',
'USER': 'SomeUser',
'PASSWORD': 'SomePassword',
'HOST': '127.0.0.1',
'PORT': '',
},
}
DATABASE_ROUTERS = ['app.dbrouters.AuthRounter']
class AuthRouter:
"""
A router to control all database operations on models in the
auth and contenttypes applications.
"""
route_app_labels = {'auth', 'contenttypes'}
def db_for_read(self, model, **hints):
"""
Attempts to read auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return 'default'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return 'default'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the auth or contenttypes apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'auth_db' database.
"""
if app_label in self.route_app_labels:
return db == 'default'
return None
models.py
class usermaster(models.Model):
userid = models.AutoField(primary_key=True, unique=True)
class Meta:
app_label = 'user_master'
I have a Django app that works with multiple databases and I'm having an issue trying to read data specifically from (testdb2) database.
I do know that you can choose which database that you want to read data using keyword (Using) but that didn't work. The result is it's always tries to read data from (testdb).
Here is the code for database in (Settings.py):
DATABASES = {
'default': {
'NAME': 'testdb',
'ENGINE': 'django.db.backends.postgresql',
'USER': 'username',
'PASSWORD': 'password',
'HOST': 'host',
'PORT': 'port',
},
'users': {
'NAME': 'testdb2',
'ENGINE': 'sql_server.pyodbc',
'USER': 'username',
'PASSWORD': 'password',
'HOST': 'host',
'PORT': 'port',
'OPTIONS': {
'driver': 'ODBC Driver 13 for SQL Server',
},
},
}
Here is the code for (Views.py):
from rest_framework import viewsets
from .models import MyModel
from .serializers import MyModelSerializer
# Create your views here.
class MyModelView(viewsets.ModelViewSet):
queryset = MyModel.objects.using('users').all()
serializer_class = MyModelSerializer
Here is the code for (Models.py):
from django.db import models
from django.db import connection
# Create your models here.
class MyModel(models.Model):
columnName = models.IntegerField(db_column='columnName', primary_key=True)
columnEmail= models.IntegerField(db_column='columnEmail')
columnAddress= models.IntegerField(db_column='columnAddress')
columnPhone= models.IntegerField(db_column='columnPhone')
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'UserTable'
MyModel is based on a view that is created inside the database.
My question: How can I read data from the database (testdb2)
Thanks :D
I think viewset uses using method only in list. Because using method is only in queryset, so don't use viewset, or use a database router.
I recommend you database router. Check Database Router Doc
In addition to Yongjin Jo's opinion, ModelViewSet in DRF inherits CreateModelMixin. And it's create function.
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
function create model instance by Serializer. So using method is not used.
You can override save method in Serializer. Or Use database router as Yongjin Jo said.
Setup:
Django 1.7 | Postgres 9.x
class Buildings(BaseModel):
number = models.CharField(max_length=25)
class TestGeneric(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
Let's say I created a TestGeneric instance, associated it with a Building and saved it:
TestGeneric.objects.create(content_object=Building.objects.first())
Now I rename Buildings to Building and run makemigrations. I'm prompted Did you rename the app.Buildings model to Building? [y/N]
I choose yes. I then run migrate and get:
The following content types are stale and need to be deleted:
app | buildings
Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
Regardless of what I answer Django automatically will create a new row in django_content_type with building as the name and label. Is there any way to just rename the ContentType so that all of my TestGeneric rows aren't blown away?
I just used this on a project; the caveat being that this works without issue if you create the migration before you've already tried to apply the automatically-created model rename migration.
You'll want to change the app name, the model names, and the previous migration to match your setup; in this example we changed the name of a model from profile to member.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from django.conf import settings
sql = """UPDATE django_content_type
SET name = 'member',
model = 'member'
WHERE name = 'profile' AND
model = 'profile' AND
app_label = 'open_humans';"""
reverse_sql = """UPDATE django_content_type
SET name = 'profile',
model = 'profile'
WHERE name = 'member' AND
model = 'member' AND
app_label = 'open_humans';"""
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('open_humans', '0004_auto_20150106_1828'),
]
operations = [
migrations.RunSQL(sql, reverse_sql)
]
I can share this migration operation written for this issue:
from django.db import migrations
from django.contrib.contenttypes.models import ContentType
class UpdateContentType(migrations.RunPython):
'''Database migration operation to update a ContentType'''
def _update_contenttype_func(self, old_app: str, old_model: str, new_app: str, new_model: str):
def func(apps, schema_editor):
ContentType.objects \
.filter(app_label=old_app, model=old_model) \
.update(app_label=new_app, model=new_model)
ContentType.objects.clear_cache()
return func
def __init__(self, app: str, model: str, new_app: str = None, new_model: str = None):
if new_app is None:
new_app = app
if new_model is None:
new_model = model
self.app = app
self.model = model
self.new_app = new_app
self.new_model = new_model
super().__init__(
code=self._update_contenttype_func(
old_app=app, old_model=model, new_app=new_app, new_model=new_model
),
reverse_code=self._update_contenttype_func(
old_app=new_app, old_model=new_model, new_app=app, new_model=model
),
)
def describe(self):
return (f"Update ContentType {self.app}.{self.model}"
f" to {self.new_app}.{self.new_model}")
Whenever a model is renamed, I edit the migration file and add an UpdateContentType operation too:
from django.db import migrations
from apps.utils.migrations_util import UpdateContentType
class Migration(migrations.Migration):
dependencies = [
('myapp', '0010_previous_migration'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.RenameModel(old_name='OldModel', new_name='NewModel'),
UpdateContentType(app='myapp', model='oldmodel', new_model='newmodel'),
]
I like both peewee and Flask security, and they both work well together, but I can't get the admin functionality of peewee to work. I am really not sure about how to do this, but I really want to use both together. I have tried a lot of things, but nothing seems to work.
My current code looks like this:
import datetime
from flask import Flask, abort, redirect, render_template, url_for, flash, request
from flask_peewee.auth import Auth, BaseUser
from flask_peewee.db import Database
from flask_peewee.admin import Admin, ModelAdmin
from flask_peewee.rest import RestAPI, UserAuthentication
from peewee import *
from flask.ext.security import *
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['DATABASE'] = {
'name': 'ee.db',
'engine': 'peewee.SqliteDatabase',
}
# Create database connection object
db = Database(app)
class Role(db.Model, RoleMixin):
name = TextField(unique=True)
description = TextField(null=True)
class RoleAdmin(ModelAdmin):
columns = ('name', 'description')
class User(db.Model, UserMixin):
username = TextField()
password = TextField()
email = TextField()
role = ForeignKeyField(Role)
active = BooleanField(default=True)
confirmed_at = DateTimeField(null=True)
class UserAdmin(ModelAdmin):
columns = ('username', 'email', 'role', 'active', 'confirmed_at')
class UserRoles(db.Model):
user = ForeignKeyField(User, related_name='roles')
role = ForeignKeyField(Role, related_name='users')
name = property(lambda self: self.role.name)
description = property(lambda self: self.role.description)
class UserRolesAdmin(ModelAdmin):
columns = ('user', 'role', 'name', 'description')
class Note(db.Model):
user = ForeignKeyField(User, related_name='notes')
title = CharField()
message = TextField()
created = DateTimeField(default=datetime.datetime.now)
def __unicode__(self):
return '%s: %s %s' % (self.user.username, self.message, self.created)
class NoteAdmin(ModelAdmin):
columns = ("user", "message", "created")
foreign_key_lookups = {'user': 'username'}
class CustomAuth(Auth):
def get_user_model(self):
return User
def get_model_admin(self):
return UserAdmin
class CustomAdmin(Admin):
def check_user_permission(self, user):
if has_role(current_user).name == "admin":
return True
# Setup Flask-Security
user_datastore = PeeweeUserDatastore(db, User, Role, UserRoles)
security = Security(app, user_datastore)
#Setup Flask-Peewee admin
auth = CustomAuth(app, db)
admin = CustomAdmin(app, auth)
admin.register(Note, NoteAdmin)
admin.register(User, UserAdmin)
admin.setup()
#create a RestAPI container
api = RestAPI(app)
#register the Note model
api.register(Note)
api.setup()
#instantiate the user auth
user_auth = UserAuthentication(auth)
# Views
#app.route("/")
#login_required
def index():
messages = Note.select()
return render_template("base.html",
notes = messages,
username = current_user.username,
role = current_user.role.name
)
#app.route("/add", methods = ['post'])
#login_required
def add():
user = current_user
now = datetime.datetime.now()
Note.create(user = current_user.get_id(), title = request.form["title"], message = request.form["message"], created = now)
flash('New entry was successfully posted')
return redirect(url_for('index'))
#app.route("/delete/<id>")
#login_required
def delete(id):
note = Note.get(Note.id == id)
note.delete_instance()
flash('Entry was successfully removed')
return redirect(url_for('index'))
if __name__ == '__main__':
for Model in (Role, User, UserRoles):
Model.drop_table(fail_silently=True)
Model.create_table(fail_silently=True)
user_datastore.create_role(name="admin", description="admin")
user_datastore.create_user(username="Ben", email='hello', password='password', active=True, confirmed_at=datetime.datetime(1935,9,9,9,9), role=1)
user_datastore.add_role_to_user('hello', "admin")
Note.create_table(fail_silently=True)
app.run(port = 8080)
When I try to log in into the Peewee Admin, I get the following error: `AttributeError: 'User' object has no attribute 'check_password'
EDIT:
Full debug:
File "/anaconda/lib/python2.7/site-packages/flask_peewee/auth.py", line 170, in login
form.password.data,
File "/anaconda/lib/python2.7/site-packages/flask_peewee/auth.py", line 128, in authenticate
if not user.check_password(password):
AttributeError: 'User' object has no attribute 'check_password
I really do not understand why this raises an AttributeError. The password is registered, and the BaseUser class is imported (required for check_password). What am I doing wrong?
Your User class needs to implement the check_password and set_password methods, because Flask-Peewee calls those to authenticate requests and update models.
Flask-Peewee provides a class, BaseUser, with simple implementations you could mix in to User, but this probably isn't useful when you're also using Flask-Security. You'll need to implement the methods yourself in a way that they call into Flask-Security to compare and hash passwords, because the two projects' approaches to password hashing are different, and you're only saving one hash in the database.
TastyPie shows here how to list ContentTypes and GenericForeignKeys. I have this working, but how do you make a POST to create a resource that has Generic Foreign Key?
This is my resource: I want to now create a new campaign while also creating a new SingleVoucherReward or MultiVoucherReward at the same time then linking it. How can this be done in TastyPie?
class CampaignCreateResource(ModelResource):
"""
API Facet.
"""
user = fields.ToOneField(UserResource, 'user', full=True)
participant_reward = GenericForeignKeyField({
SingleVoucherReward: SingleVoucherResource,
MultiVoucherReward: MultiVoucherResource,
}, 'participant_reward')
class Meta:
queryset = Campaign.objects.all()
resource_name = 'campaign'
allowed_methods = ['post', 'get']
authentication = APIAuthentication().get_authentication()
authorization = UserObjectsOnlyAuthorization()
validation = FormValidation(form_class=CampaignForm)
excludes = ['id', 'participant_reward_object_id']
def hydrate(self, bundle, request=None):
"""
Tastypie uses a 'hydrate' cycle to take serializated data from the client
and turn it into something the data model can use.
"""
bundle.obj.user = get_user_model().objects.get(pk=bundle.request.user.id)
return bundle