I'm trying to create an editable Help page for users on my website using Django v1.11 and Trumbowyg. What I'm trying to accomplish is:
- Only allowing a superuser to edit the page via a button visible to them
- Using the Trumbowyg editor to enter and edit the text
- Saving that text and then displaying it as html on the page
Naturally, I thought that I should create a model for the HelpText which would hold the text put into the Trumbowyg editor. However, this model would have to be special in that there can only be one of them. So I turned to the Singleton Model. I followed this tutorial and created a SingletonModel model and subclassed it for my HelpText model like so.
class SingletonModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.pk = 1
super(HelpText, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
pass
@classmethod
def load(cls):
obj, created = cls.objects.get_or_create(pk=1)
return obj
class HelpText(SingletonModel):
text = models.TextField(blank=True)
However, after registering HelpText to my admin.py this gave me an error.
the model s is abstract so it cannot be registered with admin.' model.__name__
From a couple of google searches, I found that the problem was that in class Meta, abstract is set to true. I could not figure out how to get rid of this error so instead I thought, "Hey! If the problem is that abstract is set to true then lets nuke the SingletonModel class completely and just put its methods into HelpText." So then I did this.
class HelpText(models.Model):
text = models.TextField(blank=True)
def save(self, *args, **kwargs):
self.pk = 1
super(HelpText, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
pass
@classmethod
def load(cls):
obj, created = cls.objects.get_or_create(pk=1)
return obj
def __str__(self):
return self.help_text
Surprisingly, I got an error for this too but of a crueler variety. Now, Django is telling me when I load my help page that "name 'self' is not defined." I have no idea how this is possible considering that I have several other models that use def__str__(self) to define their string methods and blatantly use self to refer to its own fields. How is this happening? The error also occurs in the help_landing method in views.py on the line help_text = webapp_models.HelpText.load()
. The views and templates relevant to this model are below.
views.py
@login_required(login_url=LEADER_LOGIN_URL)
def help_landing(request):
# Variable to decide if the edit button exists on the help_landing page
super_can_edit = is_superuser(request.user)
help_text = webapp_models.HelpText.load()
context = {
'include_back': True,
'include_home': True,
'description': 'Help',
'super_can_edit': super_can_edit,
'help_text': help_text,
}
return render(request, 'leader/help_landing.html', context)
@login_required(login_url=LEADER_LOGIN_URL)
def help_create_landing(request):
help_text = webapp_models.HelpText.load()
context = {
'include_back': True,
'include_home': True,
'description': 'Help Edit',
'help_text': help_text,
}
return render(request, 'leader/help_create_landing', context)
@login_required(login_url=LEADER_LOGIN_URL)
def help_create(request):
help_text = webapp_models.HelpText.load()
help_text.update(
text=request.POST['text'],
)
help_text.save()
return redirect('leader/help_landing')
help_landing.html
{% extends "leader/base.html" %}
{% block extra_css %}
<style>
button {
background-color: Transparent;
background-repeat: no-repeat;
border: none;
cursor: pointer;
overflow: hidden;
outline: none;
}
</style>
{% endblock %}
{% block description %}{{ description }}{% endblock %}
{% block content %}
<div class="ui grid">
<div class="eight wide column">
{% if help_text %}{{ help_text.text }}{% endif %}
</div>
{% if super_can_edit %}
<div class="six wide column">
<a href="leader/help_create_landing/">
<button class="customButton" type="submit">
<h2 class="ui header">
<i class="big circle add icon"></i>
Edit Help Page
</h2>
</button>
</a>
</div>
{% endif %}
</div>
{% block javascript %}
<script>
</script>
{% endblock %}
help_create_landing.html
{% extends "leader/base.html" %}
{% block extra_css %}
<style>
button {
background-color: Transparent;
background-repeat: no-repeat;
border: none;
cursor: pointer;
overflow: hidden;
outline: none;
}
</style>
{% endblock %}
{% block description %}{{ description }}{% endblock %}
{% block content %}
<div class="column">
<form name="helpForm" action="/leader/help_create/" method="post" role="form" enctype="multipart/form-data">{% csrf_token %}
<div class="ui grid">
<div class="six wide column">
<textarea id="helpEditor" name="text" rows=6 type="text">
{% if help_text %}{{ help_text.text }}{% endif %}
</textarea>
</div>
<div class="six wide column">
<button type="submit" id="save">Edit</button>
</div>
</div>
</form>
</div>
{% block javascript %}
<script>
$(helpEditor).trumbowyg();
</script>
{% endblock %}
Traceback: I'm switching out the website name for security reasons
Environment:
Request Method: GET
Request URL: https://fdev.com/leader/help_landing/
Django Version: 1.11.4
Python Version: 3.5.2
Installed Applications:
['webapp.apps.WebappConfig',
'leader.apps.LeaderConfig',
'rest_framework',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/home/felix/felix_env/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/home/felix/felix_env/lib/python3.5/site-packages/channels/handler.py" in process_exception_by_middleware
243. return super(AsgiHandler, self).process_exception_by_middleware(exception, request)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
23. return view_func(request, *args, **kwargs)
File "/home/felix/felixserver/leader/views.py" in help_landing
1892. help_text = webapp_models.HelpText.load()
File "/home/felix/felixserver/webapp/models.py" in load
Exception Type: NameError at /leader/help_landing/
Exception Value: name 'self' is not defined
When in the Django shell, I can instantiate a HelpText object but if I create more than one I get an error. I believe this is because I made sure that there should always be one HelpText model existing. Here's the traceback.
Traceback (most recent call last):
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/backends/utils.py", line 65, in execute
return self.cursor.execute(sql, params)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute
return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: webapp_helptext.id
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/query.py", line 394, in create
obj.save(force_insert=True, using=self.db)
File "/home/felix/felixserver/webapp/models.py", line 371, in save
super(HelpText, self).save(*args, **kwargs)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/base.py", line 807, in save
force_update=force_update, update_fields=update_fields)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/base.py", line 837, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/base.py", line 923, in _save_table
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/base.py", line 962, in _do_insert
using=using, raw=raw)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/query.py", line 1076, in _insert
return query.get_compiler(using=using).execute_sql(return_id)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 1107, in execute_sql
cursor.execute(sql, params)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/backends/utils.py", line 80, in execute
return super(CursorDebugWrapper, self).execute(sql, params)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/backends/utils.py", line 65, in execute
return self.cursor.execute(sql, params)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/utils.py", line 94, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/utils/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/backends/utils.py", line 65, in execute
return self.cursor.execute(sql, params)
File "/home/felix/felix_env/lib/python3.5/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: webapp_helptext.id