I need to setup multiple databases in Wagtail, but having difficulty getting the table in my secondary database to show.
I have done the following steps (code below):
1. Created a models.py file
2. Created a wagtail_hooks.py
3. Created an additional database reference in base.py
I am expecting my mysql table (brands) to show up in the Wagtail CMS, but is using the default database (sqllite). (There are no error messages returned)
Reference code:
models.py
from django.db import models
from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel
class Brand(models.Model):
brandsid = models.AutoField(primary_key=True)
brand = models.CharField(max_length=50, blank=False, null=False)
class Meta:
managed = True
db_table = 'brands'
panels = [
FieldPanel('brand'),
]
wagtail_hooks.py
from wagtail.contrib.modeladmin.options import (
ModelAdmin, modeladmin_register)
from .models import Brand
class BrandAdmin(ModelAdmin):
model = Brand
menu_label = 'Brands' # ditch this to use verbose_name_plural from model
menu_icon = 'pilcrow' # change as required
menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd)
add_to_settings_menu = False # or True to add your model to the Settings sub-menu
exclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer view
list_display = ('brand', 'brandurl',)
list_filter = ('brand',)
search_fields = ('brand', 'brandurl',)
# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(BrandAdmin)
base.py (excerpt)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'mysql': {
'NAME': 'mysql',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysqlusername',
'PASSWORD': 'mysqlpassword'
}
}
The solution here was that I needed to use the instructions from django's docs here:
https://docs.djangoproject.com/en/3.0/topics/db/multi-db/
Additional to my settings above:
I added this to base.py:
DATABASE_ROUTERS = ['routers.DbRouter']
Then created a routers.py file in my root project directory:
class DbRouter:
def db_for_read(self, model, **hints):
# print(model._meta.app_label)
if model._meta.app_label == 'brands':
return 'mysql'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'brands':
return 'mysql'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'brands' or \
obj2._meta.app_label == 'brands':
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label == 'brands':
return db == 'mysql'
return None
I was also able test that it was hitting the routers.py file by issuing a print(model._meta.app_label) command within the function, and checking the console output from runserver, which helped to get the correct settings.
Related
I would like to add permissions to ModelViewSet and restrict the current user ("Waiter") role from create and delete categories but dont know how to work with the mixins. Could you help?
Here is the view:
class CategoriesFilter(filters.FilterSet):
class Meta:
model = Categories
fields = (
'name', 'description', 'created_by__id', 'updated_by__id', 'parant__id')
class CategoriesModelViewSet(ModelViewSet):
queryset = Categories.objects.all()
serializer_class = CategoriesModelSerializer
pagination_class = LimitOffsetPagination # ?offset=0&limit=2 <= add this to the url field to test you pagination
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_class = CategoriesFilter
ordering_fields = ('name', 'parant__id')
def get_permissions(self):
if self.action in ['update', 'partial_update', 'list']:
self.permission_classes = [permissions.IsWaiter,]
return super(self.__class__, self).get_permissions()
You can follow examples on the doc here:
Write you own permission and bind to permissions classes.
This would be something like:
class CustomPerm(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
is_allowed = request.user.user_permissions.filter(yourpermquery)
# or
is_allowed = request.user.groups.filter(name='yourrole')
return is_allowed
I wanted Django to show up my table in the admin site of Django.
I did everything fine, I did both makemigration and migrate and also I registered my table in admin.py.
by the way, I use the Postgres database.
admin.py
from django.contrib import admin
from .models import Destination
admin.site.register(Destination)
models.py
from django.db import models
class Destination(models.Model):
name = models.CharField(max_length = 100)
price = models.IntegerField()
desc = models.TextField()
image = models.ImageField(upload_to='pic')
You need to create DestinationAdmin class in your admin.py
class DestinationAdmin(admin.ModelAdmin):
list_display = [
'name',
'price',
'desc',
]
admin.site.register(Destination)
Watch documentation
When I create a post request with json ex.
{
"title":"test",
"company" : "test",
"location" :"test",
"link" :"http://www.google.com/1"
}
The response I recieve is:
{"id":538,"link":"http://www.google.com/1"}
Why are not all of my fields saving to the database?
I've changed fields = '__all__' to fields = ('title', 'company', 'location', 'link') but I get an error:
TypeError at /api/listings/ Object of type TextField is not JSON
serializable
from django.db import models
# Model:
class Listing(models.Model):
title = models.TextField(max_length=100,blank=True),
company = models.TextField(max_length=50, blank=True),
location = models.TextField(max_length=50, blank=True),
link = models.URLField(max_length=250, unique=True)
#------------------------------------------------
from rest_framework import serializers
from listings.models import Listing
#Listing Serializer:
class ListingSerializer(serializers.ModelSerializer):
class Meta:
model = Listing
fields = '__all__'
#------------------------------------------------
from listings.models import Listing
from rest_framework import viewsets, permissions
from .serializers import ListingSerializer
#Listing Viewset:
class ListingViewSet(viewsets.ModelViewSet):
queryset = Listing.objects.all()
#.objects.all().delete()
permissions_classes = [
permissions.AllowAny
]
serializer_class = ListingSerializer
Ended up solving my issue by creating a new project with the same app setup. I am assuming there was some issue with the initial migration of the app model.
I have a few Django models that I display in the admin using wagtail’s modelAdmin. few of the fields in the models are referencing the user model. Since I’m not creating the form/page and it’s handled by wagtail, how do I pass the current user object to the field when it’s saved? That is, the created and updated by fields should have the current user object assigned to them.
See code snippet below, currently I'm assigning the user manually by querying, instead I'd like to get the current user.
from django.db import models
from django.conf import settings
from django.contrib.auth import get_user_model
from wagtail.admin.forms import WagtailAdminPageForm
STATUS_CHOICES = (
(1, 'Active'),
(2, 'Inactive')
)
class BasePageForm(WagtailAdminPageForm):
def save(self, commit=True):
auth_user_model = get_user_model()
default_user = auth_user_model.objects.get(username='admin')
page = super().save(commit=False)
if not page.pk:
page.created_by = default_user
page.updated_by = default_user
else:
page.updated_by = default_user
if commit:
page.save()
return page
class BaseModel(models.Model):
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='created%(app_label)s_%(class)s_related'
)
created_at = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='updated%(app_label)s_%(class)s_related'
)
updated_at = models.DateTimeField(auto_now=True)
status = models.IntegerField(choices=STATUS_CHOICES, default=1)
class Meta:
abstract = True # Set this model as Abstract
base_form_class = BasePageForm
I'm new with Django and I'm having some problems creating a custom user model. I followed every steps from the django documentation. Here is my model :
class UserProfile(models.Model):
user = models.OneToOneField(User)
comment = models.BooleanField()
score = models.IntegerField(null=True)
profilpic = models.ImageField(upload_to="/profilepics")
bio = models.CharField(max_length=140)
Then I created several users with django-registration. But when I go to the admin and I try to delete a user I created or when I just try to click on the username, I get this error:
AttributeError at /admin/auth/user/3/
'UserProfile' object has no attribute 'username'
Exception Value:
'UserProfile' object has no attribute 'username'
Exception Location: /Users/marc-antoinelacroix/Desktop/Site/sportdub/projet/models.py in __unicode__, line 14
So I think I have to create a "username" in my UserProfile model, and associate it to the username of the django's User, but I have no idea how to do it...
Any help would be welcome.
Thanks!
It seems like you're trying to access
def __unicode__(self):
return self.username
but it has to be
def __unicode__(self):
return self.user
Here's a demo
project/account/models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
homepage = models.URLField(verify_exists=False)
#...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
project/account/admin.py
from django.contrib import admin
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from account.models import UserProfile
admin.site.unregister(User)
class UserProfileInline(admin.StackedInline):
model = UserProfile
class UserProfileAdmin(UserAdmin):
inlines = [UserProfileInline]
admin.site.register(User, UserProfileAdmin)
project/settings.py
AUTH_PROFILE_MODULE = "account.userprofile"
No, you need to define UserProfile.__unicode__() properly. It needs to get the username from the related User model.