Django and MongoDB
Supporting a different set of fields for each document in a collection is one of MongoDB's features. It allows you to store similar data, but with different properties in the same collection.
for example:
{
_id: ObjectId("51156a1e056d6f966f268f81"),
type: "Article",
author: "Derick Rethans",
title: "Introduction to Document Databases with MongoDB",
date: ISODate("2013-04-24T16:26:31.911Z"),
body: "This artiā¦"
},
{
_id: ObjectId("51156a1e056d6f966f268f82"),
type: "Book",
author: "Derick Rethans",
title: "php|architect's Guide to Date and Time Programming with PHP",
isbn: "978-0-9738621-5-7"
}
Django dose not support Non-Relational data base like mongodb by default, but there are some lib's for this purpose. for example Django MongoDB Engine is a MongoDB backend for Django.
MongoDB allow to use different set of fields for each document in a collection, but in django you have to define models.py:
from django.db import models
from djangotoolbox.fields import ListField
class Post(models.Model):
title = models.CharField()
text = models.TextField()
tags = ListField()
comments = ListField()
the Question is: is there any way to define different set of fields for each document in a collection in MongoDB, when using Django ?
The Alternative
I like using django-mongoengine as it makes things clearer when dealing with MongoDB models.
For example, you can create structured Documents that are going to be transformed into models or EmbeddedDocument`s that are structured documents to be used in an already existed model.
from django_mongoengine import Document, EmbeddedDocument, fields
class Comment(EmbeddedDocument):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
author = fields.StringField(verbose_name="Name", max_length=255)
email = fields.EmailField(verbose_name="Email")
body = fields.StringField(verbose_name="Comment")
class Post(Document):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
title = fields.StringField(max_length=255)
slug = fields.StringField(max_length=255, primary_key=True)
comments = fields.ListField(
fields.EmbeddedDocumentField('Comment'), blank=True,
)
The Answer
So for your case what you need to use is Dynamic document schemas that work in the same way as Document but any data/attributes set to them will also be saved.
class Page(DynamicDocument):
title = StringField(max_length=200, required=True)
# Create a new page and add tags
>>> page = Page(title='Using MongoEngine')
>>> page.tags = ['mongodb', 'mongoengine']
>>> page.save()
>>> Page.objects(tags='mongoengine').count()
>>> 1
I struggled this problem in user profile page. This is my solution.
model.py
from django.contrib.auth.models import User
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,related_name="user_profile")
fullname = models.CharField(max_length=100,verbose_name="full name")
about = models.CharField(max_length=300,blank=True,null=True)
hobies = models.CharField(max_length=200,blank=True)
recent_aktivity = models.CharField(max_length=150,verbose_name="recent activity",null=True)
photo = models.ImageField(blank=True,null=True,upload_to="images/")
recent_badges = models.CharField(max_length=100,verbose_name="recent badges",null=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True,null=True,verbose_name="phone number")
website_url = models.URLField(blank=True,null=True,verbose_name="company website")
projects =models.CharField(max_length=200,blank=True,null=True)
bio = models.CharField(max_length=300,blank=True,null=True)
def __str__(self):
return f'{self.user.username}-ProfileModel'
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
#receiver(post_save,sender=User)
def update_user_profile(sender,instance,created,**kwargs):
if created:
profile = Profile.objects.create(user =instance)
app.py
from django.apps import AppConfig
class ProfileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Profile'
def ready(self):
import Profile.signals
forms.py
from django import forms
from.models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = '__all__'
exclude = ['user']
views.py
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.urls.base import reverse
from .forms import ProfileForm
from .models import Profile
from django.shortcuts import redirect, render,get_object_or_404
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
login_required(login_url="user:login")
def dashboard(request):
return render(request,"dashboard.html")
#login_required(login_url="user:login")
def get_profile(request):
profile = get_object_or_404(Profile,user=request.user)
return render(request,"profile.html",{"profile":profile})
I'm using Django 1.6.4 with Python 3.3. I would like to have, in my admin page, a Search menu to find a name in my table. My project is 'prova' and the model is 'pazienti'.
I used this code in models.py:
from django.db import models
from django.contrib import admin
from prova.pazienti.models import *
class Patient(models.Model):
name = models.CharField(max_length=60)
surname = models.CharField(max_length=60)
data_nascita = models.DateField()
class Admin:
list_display=('name','surname')
list_filter=('surname',)
search_fields=('surname',)
But... it doesn't work!
I tried also do not insert class Admin in models.py but insert this in admin.py:
from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _
from prova.pazienti.models import *
class PatientAdmin(admin.ModelAdmin):
list_display=('numero_centro','matricola_pz_centro')
list_filter=('numero_centro',)
search_fields=('numero_centro',)
admin.site.register(Patient, PatientAdmin)
admin.site.unregister(Patient)
But the result is the same: no error but I haven't the search bar!
Can you help me?
Thank you!
Ok, I find...
The code is correct, only in some previous exercise I insert:
admin.site.register(Patient)
in urls.py, so all things wrote in admin.py were ignored!
Sorry and thank you
I don't think that it is recognizing the existence of my fields. Here's my models.py:
from django.db.models import *
from django.contrib import admin
from django.forms import *
class Stock(Model):
name = CharField(max_length=60)
class Meta:
ordering = ["name"]
def __unicode__(self):
return self.name
admin.site.register(Stock)
When I run it, I get this error: "portfolio.stock: "ordering" refers to "name", a field that doesn't exist." When I comment the meta function out and run it, it works fine until the admin site, where when I try to create a stock object, the fields don't show up.
I'm completely confused by what's going on.
The problem is your * imports.
django.db.models.CharField is being replaced by django.forms.CharField:
>>> from django.db.models import *
>>> CharField
<class 'django.db.models.fields.CharField'>
>>> from django.forms import *
>>> CharField
<class 'django.forms.fields.CharField'>
So, actually name = CharField(max_length=60) defines a form field instead of a model one - it breaks everything and makes this bug subtle.
Solution: remove unnecessary forms import and be explicit in your imports:
from django.db import models
from django.contrib import admin
class Stock(models.Model):
name = models.CharField(max_length=60)
class Meta:
ordering = ["name"]
def __unicode__(self):
return self.name
admin.site.register(Stock)
I'm trying to add an extra input to a admin.ModelAdmin for a model I have so I can record some optional text when another input has changed.
I can't get the custom ModelForm recognised as name 'EquipmentAdmin' is not defined. I've tried several different ways of importing but think I must have missed something obvious. It feels like there's a circular reference between the EquipmentAdmin and EquipmentAdminForm as they both include a reference to each other in the code.
I have created my Django application Flightdeck and have these all in the same folder;
models.py
from django.db import models
class Location(models.Model):
name = models.CharField(max_length=45)
class Equipment(models.Model):
unit_id = models.CharField(max_length=45)
located = models.ForeignKey(Location)
located_from = models.DateField()
class EquipmentLocationHistory(models.Model):
unit = models.ForeignKey(Equipment)
located = models.ForeignKey(Location)
start = models.DateField()
end = models.DateField()
change_reason = models.CharField(max_length=45)
admin.py
from django.contrib import admin
from flightdeck.models import *
from flightdeck.forms import EquipmentAdminForm
class EquipmentAdmin(admin.ModelAdmin):
form = EquipmentAdminForm
def save_model(self, request, obj, form, change):
if 'located' in form.changed_data:
try:
old = Equipment.objects.get(unit_id=obj.unit_id)
except Equipment.DoesNotExist:
old = None
if old:
if 'located' in form.changed_data:
located_history = EquipmentLocationHistory(unit=obj, located=old.located, start=old.located_from, end=obj.located_from)
located_history.save()
obj.save()
forms.py
from django import forms
from django.contrib import admin
class EquipmentAdminForm(forms.ModelForm):
reason = forms.CharField()
class Meta:
model = EquipmentAdmin
I would like to include the reason value when I add the EquipmentLocationHistory but can't test what I have as the EquipmentAdminForm isn't loaded.
EquipmentAdmin is not a model. Your ModelForm needs to reference Equipment
from django import forms
from django.contrib import admin
from flightdeck.models import Equipment
class EquipmentAdminForm(forms.ModelForm):
reason = forms.CharField()
class Meta:
model = Equipment
PS: when you have circular references, there are many ways around the problem. The best way with model imports and django is to use django.db.models.get_model('app', 'model')
I'm attempting to work my way through Practical Django Projects. It seems to be a bit old, but I've manage to convert the code up to this point.
At this point the book would like me to change my models.py to be this:
class SearchKeyword(models.Model)
keyword = models.CharField(maxlength=50, core=True)
page = models.ForeignKey(FlatPage, edit_inline=models.STACKED,
min_num_in_admin=3, num_extra_on_change=1)
I know that this is now done in the admin.py instead. So my models.py looks like this:
from django.db import models
from django.contrib.flatpages.models import FlatPage
class SearchKeyword(models.Model):
keyword = models.CharField(max_length=50)
page = models.ForeignKey(FlatPage)
class Admin:
pass
def __unicode__(self):
return self.keyword
And the admin.py I've created now looks like this:
from search.models import SearchKeyword
from django.contrib import admin
from django.contrib.flatpages.models import FlatPage
class SearchKeywordInline(admin.StackedInline):
model = SearchKeyword
extra = 3
class FlatPageAdmin(admin.ModelAdmin):
model = FlatPage
inlines = [SearchKeywordInline]
admin.site.register(FlatPage, FlatPageAdmin)
When I load the Admin page, I receive:
AlreadyRegistered at /admin/
The model FlatPage is already registered
Exception Value:The model FlatPage is already registered
Thank you!
You have to unregister it first as the app itself ships with an admin.py
admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdmin)