Migration file don't change Site name and domain - django

I have this migration file:
from django.db import migrations
from django.contrib.sites.models import Site as DjangoSite
def change_django_site(apps, schema_editor):
site = DjangoSite.objects.get(id=1)
site.delete()
DjangoSite.objects.get_or_create(id=1, domain='www.xprende.com', name='XPrende')
def create_homepage(apps, schema_editor):
# Get models
ContentType = apps.get_model('contenttypes.ContentType')
Page = apps.get_model('wagtailcore.Page')
Site = apps.get_model('wagtailcore.Site')
HomePage = apps.get_model('home.HomePage')
# Delete the default homepage
# If migration is run multiple times, it may have already been deleted
Page.objects.filter(id=2).delete()
# Create content type for homepage model
homepage_content_type, __ = ContentType.objects.get_or_create(
model='homepage', app_label='home')
# Create a new homepage
homepage = HomePage.objects.create(
title="Home",
draft_title="Home",
slug='home',
content_type=homepage_content_type,
path='00010001',
depth=2,
numchild=0,
url_path='/home/',
)
# Create a site with the new homepage set as the root
Site.objects.create(
hostname='www.xprende.com', site_name='XPrende', root_page=homepage, is_default_site=True
)
def remove_homepage(apps, schema_editor):
# Get models
ContentType = apps.get_model('contenttypes.ContentType')
HomePage = apps.get_model('home.HomePage')
# Delete the default homepage
# Page and Site objects CASCADE
HomePage.objects.filter(slug='home', depth=2).delete()
# Delete content type for homepage model
ContentType.objects.filter(model='homepage', app_label='home').delete()
class Migration(migrations.Migration):
dependencies = [
('home', '0001_initial'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.RunPython(
create_homepage,
remove_homepage,
change_django_site,
),
]
As you can see i made a function that have to change the django site name and domain. But it doesn't do it, after apply the migrations i get the same default values:
In [3]: DjangoSite.objects.get(id=1)
Out[3]: <Site: example.com>
In [4]: DjangoSite.objects.get(id=1).name
Out[4]: 'example.com'
In [5]: DjangoSite.objects.get(id=1).domain
Out[5]: 'example.com'
In [6]: DjangoSite.objects.all()
Out[6]: <QuerySet [<Site: example.com>]>
I can't understand why, i put the sites migrations as dependencie of this migration file but still it doesn't works.
Can anyone explain me why i have this error?
Thank you.

You have to run change_django_site as separate operation:
class Migration(migrations.Migration):
dependencies = [
('home', '0001_initial'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.RunPython(change_django_site, reverse_code=migrations.RunPython.noop),
migrations.RunPython(create_homepage, reverse_code=remove_homepage),
]
and change_django_site must be
def change_django_site(apps, schema_editor):
DjangoSite = apps.get_model('sites', 'Site')
site = DjangoSite.objects.get_or_create(id=1)
site.domain='www.xprende.com'
site.name='XPrende'
site.save()

Related

Django Rest Framework Reverse() Method Fails

My main urls.py is located here ahlami -> ahlami -> urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api/accounts/', include('accounts.api.urls')),
]
My accounts app urls.py is located here ahlami -> accounts -> api -> urls.py
urlpatterns = [
path('users/<int:pk>/', views.UserView.as_view(), name='user-detail')
]
One of my accounts views.py returns
token = Token.objects.create(......)
return Response(data=AnonymousUserTokenResponseSerializer(instance=token).data)
My token model has three fields only. For simplicity, I listed one field below
class Token(rest_framework.authtoken.models.Token):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE
AnonymousUserTokenResponseSerializer is linked to the Token model and returns three json attributes
class AnonymousUserTokenResponseSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id')
user_url = reverse(viewname='user-detail')
class Meta:
model = Token
fields = ('key',
'user_id',
'user_url')
AnonymousUserTokenResponseSerializer fails because it can't identify reverse()
user_url = reverse(viewname='user-detail')
python manage.py runserver throws the error below because of the line above
django.core.exceptions.ImproperlyConfigured: The included URLconf
'ahlami.urls' does not appear to have any patterns in it. If you see
valid patterns in the file then the issue is probably caused by a
circular import.
My settings is located here ahlami -> ahlami -> settings -> base.py and base.py has this
ROOT_URLCONF = 'ahlami.urls'
I expect to get an output that looks like but couldn't because of the error above.
{
"key": "891e388399f2fcae016fe6887107034239041478",
"user_id": 29,
"user_url": http://localhost/api/accounts/users/29
}
How can I resolve this error and make reverse() work?
django.core.exceptions.ImproperlyConfigured: The included URLconf
'ahlami.urls' does not appear to have any patterns in it. If you see
valid patterns in the file then the issue is probably caused by a
circular import.
Use serializers.HyperlinkedIdentityField
class AnonymousUserTokenResponseSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id')
user_url = serializers.HyperlinkedIdentityField(
view_name='user-detail',
source='user.id',
lookup_field='pk'
)
class Meta:
model = Token
fields = ('key', 'user_id', 'user_url')

Django Nested Admin returns 404 or doesn't inline models in django admin area

I'm trying to get django nested admin to work, but I'm having a few issues and I'm sure I'm just making a silly mistake. Here are the steps that I followed:
Step 1: I did a pip install
Step 2: I added it to the bottom of my Installed Apps in my settings.py
Step 3: I added it to my URL array:
Their Example:
urlpatterns = patterns('',
# ...
url(r'^_nested_admin/', include('nested_admin.urls')),
)
My implementation:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("estimate_maker.urls")),
path('nested_admin/', include('nested_admin.urls')),
]
Step 4: I created a static folder in my settings.py
Step 5: I ran the collectstatic command
Step 6: I set up my admin.py in my project folder:
from django.contrib import admin
from .models import MoldInspection, MoldService
import nested_admin
class MoldServiceInline(nested_admin.NestedStackedInline):
model = MoldService
class MoldInspectionInline(nested_admin.NestedModelAdmin):
model = MoldService
sortable_field_name = "position"
inlines = [MoldServiceInline]
admin.site.register(MoldInspection)
admin.site.register(MoldService)
I do get a warning from pycharm saying the below that I'm not sure how to diagnose as I'm setting up the class as it is done in the guide.
Cannot find reference 'NestedModelAdmin' in '__init__.py'
Looking in the referenced __init__.py I see:
# import mapping to objects in other modules
all_by_module = {
'nested_admin.forms': (
'SortableHiddenMixin'),
'nested_admin.formsets': (
'NestedInlineFormSet', 'NestedBaseGenericInlineFormSet'),
'nested_admin.nested': (
'NestedModelAdmin', 'NestedModelAdminMixin', 'NestedInlineAdminFormset',
'NestedInlineModelAdmin', 'NestedStackedInline', 'NestedTabularInline',
'NestedInlineModelAdminMixin', 'NestedGenericInlineModelAdmin',
But when I update my admin.py to:
class MoldInspectionInline(nested_admin.nested.NestedModelAdmin):
I get the same error, this time pointing to "nested."
When I try to access the nested admin by either going to /nested-admin, I just get a 404 with this error message:
Using the URLconf defined in app.urls, Django tried these URL patterns, in this order:
admin/
[name='home']
nested-admin ^server-data\.js$ [name='nesting_server_data']
And when I go to /admin it looks the same as it did before.
A few more details:
I want my MoldService to exist just to be a parent for children services so I have it set up like this:
class MoldService(models.Model):
title = "Mold Services"
def __str__(self):
return self.title
I then have my child class set up like this:
class MoldInspection(models.Model):
title = "Mold Inspection"
description = models.TextField(null=True)
def __str__(self):
return self.description
Why do you think the nested admin isn't working for me?
In the referenced __init__.py file it contains the following comment
# All this craziness is so that we can allow the classes in nested_admin.formsets
# to be importable directly from this module
I guessed that something going on here is the issue, so I just deleted the contents of __init__.py and then imported with
from nested_admin.nested import *
works now!

Remove old permissions in django

In my Django site there are some permissions entries linked to applications that I've removed. For example I have permissions entries linked to "Dashboard" and "Jet" applications. How can you remove them?
Permissions have foreign keys to content types under the hood, so removing the content types for the models that no longer exist will also remove the permissions for those models.
Fortunately, Django also provides a manage.py command to remove old content types: remove_stale_contenttypes. Running that command will list the content types that no longer exist and the related objects (including permissions) that will be deleted, allowing you to review the changes and approve them.
$ manage.py remove_stale_contenttypes
Some content types in your database are stale and can be deleted.
Any objects that depend on these content types will also be deleted.
The content types and dependent objects that would be deleted are:
- Content type for stale_app.removed_model
- 4 auth.Permission object(s)
This list doesn't include any cascade deletions to data outside of Django's
models (uncommon).
Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
Type 'yes' to continue, or 'no' to cancel:
To start, make an empty migration file:
python manage.py makemigrations --empty yourappname
Change the migration (this is an example, adjust to your needs):
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def add_permissions(apps, schema_editor):
pass
def remove_permissions(apps, schema_editor):
"""Reverse the above additions of permissions."""
ContentType = apps.get_model('contenttypes.ContentType')
Permission = apps.get_model('auth.Permission')
content_type = ContentType.objects.get(
model='somemodel',
app_label='yourappname',
)
# This cascades to Group
Permission.objects.filter(
content_type=content_type,
codename__in=('add_somemodel', 'change_somemodel', 'delete_somemodel'),
).delete()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(remove_permissions, add_permissions),
]
I did it this way:
import re
for perm in Permission.objects.all():
if re.match( r".+modelname.+permissionname.+",str(perm)):
print(perm)
perm.delete()
If you have custom or model based (default) permissions you wish to remove you could write a command like this to accomplish this task:
from django.conf import settings
from django.contrib.auth.models import Permission
from django.core.management.base import BaseCommand
import django.apps
class Command(BaseCommand):
help = 'Remove custom permissions that are no longer in models'
def handle(self, *args, **options):
# get the db name needed for removal...
database_name = input('Database Name: ')
default_perm_names = list()
# are real perms in db, may not be accurate
db_custom_perm_names = list()
# will be used to ensure they are correct.
meta_custom_perm_names = list()
default_and_custom_perms = list()
for model in django.apps.apps.get_models():
# add to models found to fix perms from removed models
app_label = model._meta.app_label
lower_model_name = model._meta.model_name
all_model_permissions = Permission.objects.using(database_name).filter(content_type__app_label=app_label, content_type__model=lower_model_name)
default_and_custom_perms.extend([x for x in all_model_permissions])
# get the custom meta permissions, these should be in the meta of the class
# will be a list or tuple or list, [0=codename, 1=name]
meta_permissions = model._meta.permissions
if meta_permissions:
for perm in all_model_permissions:
# will be the model name from the content type, this is how django makes default perms
# we are trying to remove them so now we can figure out which ones are default by provided name
model_name_lower = perm.content_type.name
# default_perms = ['add', 'change', 'view', 'delete', 'undelete']
# append them to the list of default names
default_perm_names.append(f'Can add {model_name_lower}')
default_perm_names.append(f'Can change {model_name_lower}')
default_perm_names.append(f'Can view {model_name_lower}')
default_perm_names.append(f'Can delete {model_name_lower}')
default_perm_names.append(f'Can undelete {model_name_lower}')
# will mean this is a custom perm...so add it
if not perm.name in default_perm_names:
db_custom_perm_names.append(perm.name)
# the perms to ensure are correct...
for model_perm in meta_permissions:
# get the meta perm, will be a list or tuple or list, [0=codename, 1=name]
custom_perm = Permission.objects.using(database_name).get(codename=model_perm[0], name=model_perm[1])
meta_custom_perm_names.append(custom_perm.name)
perms_to_remove = [perm for perm in db_custom_perm_names if perm not in meta_custom_perm_names]
if not perms_to_remove:
print('There are no stale custom permissions to remove.')
# print(perms_to_remove)
# now remove the custom permissions that were removed from the model
for actual_permission_to_remove in Permission.objects.using(database_name).filter(name__in=perms_to_remove):
# print(actual_permission_to_remove)
actual_permission_to_remove.delete(using=database_name)
print(actual_permission_to_remove, '...deleted')
for perm in [x for x in Permission.objects.using(database_name)]:
# loop all perms...if it is not in the model perms it does not exist...
if perm.content_type.model not in [x.content_type.model for x in default_and_custom_perms]:
perm.delete(using=database_name)
print(perm, 'regular permission...deleted')
If you also wish to ensure that the default permissions are added from Django you can add this snippet in the command:
from django.apps import apps
from django.contrib.auth.management import create_permissions
from apps.client.models import ClientInformation
# add all permissions the django way
# get the db name needed from settings.py
database_name = 'default' # or whatever DB you are looking for
print(f'adding all permissions if not there to {database_name}')
for app_config in apps.get_app_configs():
# print(app_config)
app_config.models_module = True
create_permissions(app_config, using=database_name)
app_config.models_module = None
Then call via python manage.py fix_permissions if you name your command file fix_permissions.py
I've reworked #ViaTech's code to use Django's contrib.auth.management._get_all_permissions() functions which makes it more straight forward:
from typing import List, Set, Tuple
import django.apps
# noinspection PyProtectedMember
from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from django.db import DEFAULT_DB_ALIAS
class Command(BaseCommand):
help = "Remove custom permissions that are no longer defined in models"
def add_arguments(self, parser):
parser.add_argument(
"--database",
default=DEFAULT_DB_ALIAS,
help=f'Specifies the database to use. Default is "{DEFAULT_DB_ALIAS}".',
)
parser.add_argument(
"--dry",
action="store_true",
help="Do a dry run not actually deleting any permissions",
)
def handle(self, *args, **options) -> str:
using = options["database"]
# This will hold the permissions that models have defined,
# i.e. default permissions plus additional custom permissions:
# (content_type.pk, codename)
defined_perms: List[Tuple[int, str]] = []
for model in django.apps.apps.get_models():
ctype = ContentType.objects.db_manager(using).get_for_model(
model, for_concrete_model=False
)
# noinspection PyProtectedMember
for (codename, _) in _get_all_permissions(model._meta):
defined_perms.append((ctype.id, codename))
# All permissions in current database (including stale ones)
all_perms = Permission.objects.using(using).all()
stale_perm_pks: Set[int] = set()
for perm in all_perms:
if (perm.content_type.pk, perm.codename) not in defined_perms:
stale_perm_pks.add(perm.pk)
self.stdout.write(f"Delete permission: {perm}")
# Delete all stale permissions
if options["dry"]:
result = f"DRY RUN: {len(stale_perm_pks)} stale permissions NOT deleted"
else:
if stale_perm_pks:
Permission.objects.filter(pk__in=stale_perm_pks).delete()
result = f"{len(stale_perm_pks)} stale permissions deleted"
return result

Sphinx-apidoc strange output for django app/models.py

I get some missed info in generated documentation of django project, for example first_name and last_name, email are missed (although they are defined in a parent abstract class). How to control what gets added into documentation based on sphinx-apidoc scan? My goal is to auto-generate the docs based on documentation, but it seems that sphinx-apidoc is supposed to be used only one time for initial scaffolding
I tried to use :inherited-members: as shown below but it still didn't produce first_name, last_name, email that exist in AbstractUser class
.. automodule:: apps.users.models
:members:
:inherited-members:
:show-inheritance:
I execute the following command
sphinx-apidoc -f -e -d 2 -M -o docs/code apps '*tests*' '*migrations*'
Output
my apps/users/models.py
from django.contrib.auth.models import AbstractUser
from django.contrib.postgres.fields import HStoreField
from imagekit import models as imagekitmodels
from imagekit.processors import ResizeToFill
from libs import utils
# Solution to avoid unique_together for email
AbstractUser._meta.get_field('email')._unique = True
def upload_user_media_to(instance, filename):
"""Upload media files to this folder"""
return '{}/{}/{}'.format(instance.__class__.__name__.lower(), instance.id,
utils.get_random_filename(filename))
__all__ = ['AppUser']
class AppUser(AbstractUser):
"""Custom user model.
Attributes:
avatar (file): user's avatar, cropeed to fill 300x300 px
notifications (dict): settings for notifications to user
"""
avatar = imagekitmodels.ProcessedImageField(
upload_to=upload_user_media_to,
processors=[ResizeToFill(300, 300)],
format='PNG',
options={'quality': 100},
editable=False,
null=True,
blank=False)
notifications = HStoreField(null=True)
# so authentication happens by email instead of username
# and username becomes sort of nick
USERNAME_FIELD = 'email'
# Make sure to exclude email from required fields if authentication
# is done by email
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.username
class Meta:
verbose_name = 'User'
verbose_name_plural = 'Users'
My sphinx conf.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import django
import sphinx_py3doc_enhanced_theme
sys.path.insert(0, os.path.abspath('../'))
sys.path.insert(0, os.path.abspath('.'))
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE", "config.settings.local")
django.setup()
# Extensions
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.viewcode',
'sphinxcontrib.blockdiag'
]
napoleon_google_docstring = True
napoleon_use_param = True
napoleon_use_ivar = False
napoleon_use_rtype = True
napoleon_include_special_with_doc = False
# RST support
source_suffix = '.rst'
# Name of master doc
master_doc = 'index'
# General information about the project.
project = 'crm'
copyright = '2017, Company'
author = 'Company'
version = '0.1'
release = '0.1'
language = None
exclude_patterns = []
todo_include_todos = False
# Read the docs theme
html_theme = 'sphinx_py3doc_enhanced_theme'
html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()]
html_static_path = []
htmlhelp_basename = 'crmdoc'
latex_elements = {}
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'crm', 'crm Documentation',
[author], 1)
]
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'crm', 'crm Documentation',
author, 'crm', 'One line description of project.',
'Miscellaneous'),
]
html_theme_options = {
'githuburl': 'https://github.com/ionelmc/sphinx-py3doc-enhanced-theme/',
'bodyfont': '"Lucida Grande",Arial,sans-serif',
'headfont': '"Lucida Grande",Arial,sans-serif',
'codefont': '"Deja Vu Sans Mono",consolas,monospace,sans-serif',
'linkcolor': '#0072AA',
'visitedlinkcolor': '#6363bb',
'extrastyling': False,
'sidebarwide': True
}
pygments_style = 'friendly'
html_context = {
'css_files': ['_static/custom.css'],
}
Okay turned out that I had to use :undoc-members: but it created a mess.
This is required since django's AbstractUser class is not properly documented and sphinx has to be forced to display fields only with undoc-members defined. But undoc-members cause a mess, so the solution is just to add documentation in docstr of the child class for attributes/methods that have not been documented in parent class, after that my documentation got these fields displayed
class AppUser(AbstractUser):
"""Custom user model.
Attributes:
avatar (file): user's avatar, cropeed to fill 300x300 px
notifications (dict): settings for notifications to user
first_name (str): first name
last_name (str): last name
"""

How to use Django (django-mentions)

Has anyone succesfully used the django-mentions lib - https://pypi.python.org/pypi/django-mentions. If so can you help me with the issue below (thanks). I followed the documentation (but it looks like I'm not doing it right). I am getting this error message urlize_mentions in my template
TemplateSyntaxError at /
Invalid filter: 'urlize_mentions'
Request Method: GET
Request URL: http://localhost:8000/
Django Version: 1.6.6
Exception Type: TemplateSyntaxError
Exception Value:
Invalid filter: 'urlize_mentions'
Here are the steps I followed
Step 1. pip install django-mentions
Step 2. Add 'mentions' in the INSTALLED_APPS in the settings.py
INSTALLED_APPS = (
...
'mentions',
...
)
Step 3. Add mentions to the url patterns
urlpatterns = patterns('',
...
url(r'^mentions/', include('mentions.urls')),
...
)
Step 4. Implement a mention provider, I created a mentionsprovider.py in my app
from mentions.mentions import Provider
from django.contrib.auth.models import User
#Implement provider
class UserProvider(Provider):
model = User
def get_title(self, obj):
return obj.username
def search(self, request, term):
return self.get_queryset().filter(username__istartswith=term)
Step 5. Add this mentions provider in the settings.py
MENTIONS_PROVIDERS = {
# You can put your provider anywhere you want
'default': [
'appname.mentionsprovider.UserProvider'
]
}
Step 6. Use mentions.forms.MentionTextArea. I did this in my app's forms.py
from mentions.forms import MentionTextarea
class NoteForm(forms.ModelForm):
class Meta:
model = Note
widgets = {
'text': MentionTextarea
}
In my template I have something like this (i tried various combinations)
{{form.note|urlize_mentions}}
{{form.text|urlize_mentions}}
{{note.text|urlize_mentions}}