Giter Club home page Giter Club logo

django-dbsettings's Introduction


Storing settings in the database

Not all settings belong in settings.py, as it has some particular limitations:

  • Settings are project-wide. This not only requires apps to clutter up settings.py, but also increases the chances of naming conflicts.
  • Settings are constant throughout an instance of Django. They cannot be changed without restarting the application.
  • Settings require a programmer in order to be changed. This is true even if the setting has no functional impact on anything else.

Many applications find need to overcome these limitations, and dbsettings provides a convenient way to do so.

The main goal in using this application is to define a set of placeholders that will be used to represent the settings that are stored in the database. Then, the settings may be edited at run-time using the provided editor, and all Python code in your application that uses the setting will receive the updated value.

Requirements

Dbsettings Python Django
==1.3 3.8 - 3.10 2.1 - 4.0
3.6 - 3.7 2.1 - 3.2
3.5 2.1 - 2.2
==1.2 3.5 2.1 - 2.2
3.6 - 3.8 2.1 - 3.2
1.0 - 1.1.0 3.5 2.0 - 2.2
3.6 - 3.8 2.0 - 3.0
==0.11 3.5 - 3.7 1.10 - 2.2
2.7 1.10 - 1.11
==0.10 3.4 - 3.5 1.7 - 1.10
3.2 - 3.3 1.7 - 1.8
2.7 1.7 - 1.10
==0.9 3.4 - 3.5 1.7 - 1.9
3.2 - 3.3 1.7 - 1.8
2.7 1.7 - 1.9
==0.8 3.2 1.5 - 1.8
2.7 1.4 - 1.8
2.6 1.4 - 1.6
==0.7 3.2 1.5 - 1.7
2.7 1.3 - 1.7
2.6 1.3 - 1.6
==0.6 3.2 1.5
2.6 - 2.7 1.3 - 1.5
<=0.5 2.6 - 2.7 1.2* - 1.4

* Possibly version below 1.2 will work too, but not tested.

Installation

To install the dbsettings package, simply place it anywhere on your PYTHONPATH.

Project settings

In order to setup database storage, and to let Django know about your use of dbsettings, simply add it to your INSTALLED_APPS setting, like so:

INSTALLED_APPS = (
    ...
    'dbsettings',
    ...
)

If your Django project utilizes sites framework, all setting would be related to some site. If sites are not present, settings won't be connected to any site (and sites framework is no longer required since 0.8.1).

You can force to do (not) use sites via DBSETTINGS_USE_SITES = True / False configuration variable (put it in project's settings.py).

By default, values stored in database are limited to 255 characters per setting. You can change this limit with DBSETTINGS_VALUE_LENGTH configuration variable. If you change this value after migrations were run, you need to manually alter the dbsettings_setting table schema.

URL Configuration

In order to edit your settings at run-time, you'll need to configure a URL to access the provided editors. You'll just need to add a single line, defining the base URL for the editors, as dbsettings has its own URLconf to handle the rest. You may choose any location you like:

urlpatterns = patterns('',
    ...
    (r'^settings/', include('dbsettings.urls')),
    ...
)

or (django 2):

from django.urls import path, include

urlpatterns = [
    ...
    path('settings/', include('dbsettings.urls')),
    ...
]

A note about caching

This framework utilizes Django's built-in cache framework, which is used to minimize how often the database needs to be accessed. During development, Django's built-in server runs in a single process, so all cache backends will work just fine.

Most productions environments, including mod_python, FastCGI or WSGI, run multiple processes, which some backends don't fully support. When using the simple or locmem backends, updates to your settings won't be reflected immediately in all workers, causing your application to ignore the new changes.

No other backends exhibit this behavior, but since simple is the default, make sure to specify a proper backend when moving to a production environment.

Alternatively you can disable caching of settings by setting DBSETTINGS_USE_CACHE = False in settings.py. Beware though: every access of any setting will result in database hit.

By default, entries are cached for a time that is default for the cache. You can override this by providing DBSETTINGS_CACHE_EXPIRATION. Values interpreted as seconds, 0 means no caching and None means indefinite caching. See more.

Usage

These database-backed settings can be applied to any model in any app, or even in the app itself. All the tools necessary to do so are available within the dbsettings module. A single import provides everything you'll need:

import dbsettings

Defining a group of settings

Settings are be defined in groups that allow them to be referenced together under a single attribute. Defining a group uses a declarative syntax similar to that of models, by declaring a new subclass of the Group class and populating it with values.

class ImageLimits(dbsettings.Group):
    maximum_width = dbsettings.PositiveIntegerValue()
    maximum_height = dbsettings.PositiveIntegerValue()

You may name your groups anything you like, and they may be defined in any module. This allows them to be imported from common applications if applicable.

Defining individual settings

Within your groups, you may define any number of individual settings by simply assigning the value types to appropriate names. The names you assign them to will be the attribute names you'll use to reference the setting later, so be sure to choose names accordingly.

For the editor, the default description of each setting will be retrieved from the attribute name, similar to how the verbose_name of model fields is retrieved. Also like model fields, however, an optional argument may be provided to define a more fitting description. It's recommended to leave the first letter lower-case, as it will be capitalized as necessary, automatically.

class EmailOptions(dbsettings.Group):
    enabled = dbsettings.BooleanValue('whether to send emails or not')
    sender = dbsettings.StringValue('address to send emails from')
    subject = dbsettings.StringValue(default='SiteMail')

For more descriptive explanation, the help_text argument can be used. It will be shown in the editor.

The default argument is very useful - it specify an initial value of the setting.

In addition, settings may be supplied with a list of available options, through the use of of the choices argument. This works exactly like the choices argument for model fields, and that of the newforms ChoiceField. When using choices, a get_FOO_display method is added to settings, for example:

country = dbsettings.StringValue(choices=(("USA", "United States"), ("BR", "Brazil")))

>>> settings.country
"USA"
>>> settings.get_country_display()
"United States"

The widget used for a value can be overriden using the widget keyword. For example:

payment_instructions = dbsettings.StringValue(
    help_text="Printed on every invoice.",
    default="Payment to Example XYZ\nBank name here\nAccount: 0123456\nSort: 01-02-03",
    widget=forms.Textarea
)

A full list of value types is available later in this document, but the process and arguments are the same for each.

Assigning settings

Once your settings are defined and grouped properly, they must be assigned to a location where they will be referenced later. This is as simple as instantiating the settings group in the appropriate location. This may be at the module level or within any standard Django model.

Group instance may receive one optional argument: verbose name of the group. This name will be displayed in the editor.

email = EmailOptions()

class Image(models.Model):
    image = models.ImageField(upload_to='/upload/path')
    caption = models.TextField()

    limits = ImageLimits('Dimension settings')

Multiple groups may be assigned to the same module or model, and they can even be combined into a single group by using standard addition syntax:

options = EmailOptions() + ImageLimits()

To separate and tag settings nicely in the editor, use verbose names:

options = EmailOptions('Email') + ImageLimits('Dimesions')

Database setup

A single model is provided for database storage, and this model must be installed in your database before you can use the included editors or the permissions that will be automatically created. This is a simple matter of running manage.py syncdb or manage.py migrate now that your settings are configured.

This step need only be repeate when settings are added to a new application, as it will create the appropriate permissions. Once those are in place, new settings may be added to existing applications with no impact on the database.

Using your settings

Once the above steps are completed, you're ready to make use of database-backed settings.

Editing settings

When first defined, your settings will default to None (or False in the case of BooleanValue), so their values must be set using one of the supplied editors before they can be considered useful (however, if the setting had the default argument passed in the constructor, its value is already useful - equal to the defined default).

The editor will be available at the URL configured earlier. For example, if you used the prefix of 'settings/', the URL /settings/ will provide an editor of all available settings, while /settings/myapp/ would contain a list of just the settings for myapp.

URL patterns are named: 'site_settings' and 'app_settings', respectively.

The editors are restricted to staff members, and the particular settings that will be available to users is based on permissions that are set for them. This means that superusers will automatically be able to edit all settings, while other staff members will need to have permissions set explicitly.

Accessing settings in Python

Once settings have been assigned to an appropriate location, they may be referenced as standard Python attributes. The group becomes an attribute of the location where it was assigned, and the individual values are attributes of the group.

If any settings are referenced without being set to a particular value, they will default to None (or False in the case of BooleanValue, or whatever was passed as default). In the following example, assume that EmailOptions were just added to the project and the ImageLimits were added earlier and already set via editor.

>>> from myproject.myapp import models

# EmailOptions are not defined
>>> models.email.enabled
False
>>> models.email.sender
>>> models.email.subject
'SiteMail'  # Since default was defined

# ImageLimits are defined
>>> models.Image.limits.maximum_width
1024
>>> models.Image.limits.maximum_height
768

These settings are accessible from any Python code, making them especially useful in model methods and views. Each time the attribute is accessed, it will retrieve the current value, so your code doesn't need to worry about what happens behind the scenes.

def is_valid(self):
    if self.width > Image.limits.maximum_width:
        return False
    if self.height > Image.limits.maximum_height:
        return False
return True

As mentioned, views can make use of these settings as well.

from myproject.myapp.models import email

def submit(request):

    ...
    # Deal with a form submission
    ...

    if email.enabled:
        from django.core.mail import send_mail
    send_mail(email.subject, 'message', email.sender, [request.user.email])

Settings can be not only read, but also written. The admin editor is more user-friendly, but in case code need to change something:

from myproject.myapp.models import Image

def low_disk_space():
    Image.limits.maximum_width = Image.limits.maximum_height = 200

Every write is immediately commited to the database and proper cache key is deleted.

A note about model instances

Since settings aren't related to individual model instances, any settings that are set on models may only be accessed by the model class itself. Attempting to access settings on an instance will raise an AttributeError.

Triggering actions on settings changes

A signal is sent whenever a setting changes. You can receive it by doing something like this in your appconfig's ready() method:

from dbsettings.loading import get_setting
from dbsettings.signals import setting_changed

setting_changed.connect(my_function, sender=get_setting('myapp', 'MyClass', 'myattr'))

my_function will be called with a sender and value parameters, the latter containing a new value assigned to the setting.

Value types

There are several various value types available for database-backed settings. Select the one most appropriate for each individual setting, but all types use the same set of arguments.

BooleanValue

Presents a checkbox in the editor, and returns True or False in Python.

DurationValue

Presents a set of inputs suitable for specifying a length of time. This is represented in Python as a timedelta object.

FloatValue

Presents a standard input field, which becomes a float in Python.

IntegerValue

Presents a standard input field, which becomes an int in Python.

PercentValue

Similar to IntegerValue, but with a limit requiring that the value be between 0 and 100. In addition, when accessed in Python, the value will be divided by 100, so that it is immediately suitable for calculations.

For instance, if a myapp.taxes.sales_tax was set to 5 in the editor, the following calculation would be valid:

>>> 5.00 * myapp.taxes.sales_tax
0.25

PositiveIntegerValue

Similar to IntegerValue, but limited to positive values and 0.

StringValue

Presents a standard input, accepting any text string up to 255 (or DBSETTINGS_VALUE_LENGTH) characters. In Python, the value is accessed as a standard string.

DateTimeValue

Presents a standard input field, which becomes a datetime in Python.

User input will be parsed according to DATETIME_INPUT_FORMATS setting.

In code, one can assign to field string or datetime object:

# These two statements has the same effect
myapp.Feed.next_feed = '2012-06-01 00:00:00'
myapp.Feed.next_feed = datetime.datetime(2012, 6, 1, 0, 0, 0)

DateValue

Presents a standard input field, which becomes a date in Python.

User input will be parsed according to DATE_INPUT_FORMATS setting.

See DateTimeValue for the remark about assigning.

TimeValue

Presents a standard input field, which becomes a time in Python.

User input will be parsed according to TIME_INPUT_FORMATS setting.

See DateTimeValue for the remark about assigning.

ImageValue

(requires PIL or Pillow imaging library to work)

Allows to upload image and view its preview.

ImageValue has optional keyword arguments:

  • upload_to specifies path (relative to MEDIA_ROOT), where uploaded images will be stored. If argument is not present, files will be saved directly under MEDIA_ROOT.
  • delete_old (default to True) controls whether to delete the old file when the value has changed

In Python, the value is accessed as a standard string (file name, relative to MEDIA_ROOT).

PasswordValue

Presents a standard password input. Retain old setting value if not changed.

Setting defaults for a distributed application

Distributed applications often have need for certain default settings that are useful for the common case, but which may be changed to suit individual installations. For such cases, a utility is provided to enable applications to set any applicable defaults.

Living at dbsettings.utils.set_defaults, this utility is designed to be used within the app's management.py. This way, when the application is installed using syncdb/migrate, the default settings will also be installed to the database.

The function requires a single positional argument, which is the models module for the application. Any additional arguments must represent the actual settings that will be installed. Each argument is a 3-tuple, of the following format: (class_name, setting_name, value).

If the value is intended for a module-level setting, simply set class_name to an empty string. The value for setting_name should be the name given to the setting itself, while the name assigned to the group isn't supplied, as it isn't used for storing the value.

For example, the following code in management.py would set defaults for some of the settings provided earlier in this document:

from django.conf import settings
from dbsettings.utils import set_defaults
from myproject.myapp import models as myapp

set_defaults(myapp,
    ('', 'enabled', True)
    ('', 'sender', settings.ADMINS[0][1]) # Email of the first listed admin
    ('Image', 'maximum_width', 800)
    ('Image', 'maximum_height', 600)
)

Changelog

1.3.0 (19/02/2022)
  • Added compatibility with Django 4.0 (thanks nerdoc)
  • Fixed misleading message (about permissions) in the editor if no settings were defined
  • Fixed bug with app-level settings not present in the editor if verbose_name was not set
1.2.0 (16/12/2021)
  • Fixed exception handling in cache operation and added DBSETTINGS_CACHE_EXPIRATION (thanks AminHP)
  • Added get_FOO_display method for fields which have choices (thanks paulogiacomelli)
  • Use Django storage framework for ImageValue instead of manual filesystem handling (thanks j0nm1)
  • Dropped compatibility with Django 2.0
1.1.0 (21/03/2020)
  • Fixed image widget in Django 2.1
  • Added delete_old parameter to ImageValue
  • Make upload_to parameter to ImageValue no longer required.
  • Fix PasswordValue to not render widget as required.
1.0.1 (26/12/2019)
  • Introduced a signal setting_changed
  • Added compatibility with Django 3.0
  • Dropped compatibility with Django 1.10, 1.11
  • Dropped compatibility with Python 2
0.11.0 (31/07/2019)
  • Added compatibility with Django 1.11, 2.0, 2.1, 2.2
  • Dropped compatibility with Django 1.7, 1.8, 1.9
0.10.0 (25/09/2016)
  • Added compatibility with Django 1.10
0.9.3 (02/06/2016)
  • Fixed (hopefully for good) problem with ImageValue in Python 3 (thanks rolexCoder)
0.9.2 (01/05/2016)
  • Fixed bug when saving non-required settings
  • Fixed problem with ImageValue in Python 3 (thanks rolexCoder)
0.9.1 (10/01/2016)
  • Fixed Sites app being optional (thanks rolexCoder)
0.9.0 (25/12/2015)
  • Added compatibility with Django 1.9 (thanks Alonso)
  • Dropped compatibility with Django 1.4, 1.5, 1.6
0.8.2 (17/09/2015)
  • Added migrations to distro
  • Add configuration option to change max length of setting values from 255 to whatever
  • Add configuration option to disable caching (thanks nwaxiomatic)
  • Fixed PercentValue rendering (thanks last-partizan)
0.8.1 (21/06/2015)
  • Made django.contrib.sites framework dependency optional
  • Added migration for app
0.8.0 (16/04/2015)
  • Switched to using django.utils.six instead of standalone six.
  • Added compatibility with Django 1.8
  • Dropped compatibility with Django 1.3
0.7.4 (24/03/2015)
  • Added default values for fields.
  • Fixed Python 3.3 compatibility
  • Added creation of folders with ImageValue
0.7.3, 0.7.2
pypi problems
0.7.1 (11/03/2015)
  • Fixed pypi distribution.
0.7 (06/07/2014)
  • Added PasswordValue
  • Added compatibility with Django 1.6 and 1.7.
0.6 (16/09/2013)
  • Added compatibility with Django 1.5 and python3, dropped support for Django 1.2.
  • Fixed permissions: added permission for editing non-model (module-level) settings
  • Make PIL/Pillow not required in setup.py
0.5 (11/10/2012)
  • Fixed error occuring when test are run with LANGUAGE_CODE different than 'en'
  • Added verbose_name option for Groups
  • Cleaned code
0.4.1 (02/10/2012)
  • Fixed Image import
0.4 (30/09/2012)
  • Named urls
  • Added polish translation
0.3 (04/09/2012)
Included testrunner in distribution
0.2 (05/07/2012)
  • Fixed errors appearing when module-level and model-level settings have same attribute names
  • Corrected the editor templates admin integration
  • Updated README
0.1 (29/06/2012)
Initial PyPI release

django-dbsettings's People

Contributors

a1tus avatar alfredo avatar aminhp avatar andybak avatar danielroseman avatar djm avatar elidickinson avatar gulopine avatar hdg700 avatar j0nm1 avatar johnpaulett avatar konratt avatar lexich avatar mlier avatar natureshadow avatar nerdoc avatar paulogiacomelli avatar sciyoshi avatar sergey-zakharov avatar steinerkelvin avatar suvit avatar tigorc avatar wkleinheerenbrink avatar zlorf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-dbsettings's Issues

ForeignKey value

I was wondering whether it wouldn't be possible to have a setting holding a foreign key, or a reference to some model.

The use case is that I have a model defining a collection of objects, of which a single one can be active in the application at any time. I need to switch there in a setting.

WIlling to propose a PR if the maintainer doesn't object to it by design.

settings as models in admin panel view

1st question is, pip installing 1 or 2?

  1. https://github.com/zlorf/django-dbsettings/
  2. https://github.com/saxix/django-dbsettings/

2nd question is about possiblity to present options as standard table template as models in admin panel view (as separated models or agregated in model but as 2 tables in concept of
options = Options1("options 1") + options = Options2("options 2") where Options1 is first table and Options2 second table.
Why this? Well.. if we have many vars then need filter to search this eq. by true/false, not ctr+f thats if horrible, and select all functions to change true/false is nice. Adding vars in table look nice, filtering, etc, and logs about changes.
Soo.. maybe django-dbsettings-2 is need? Any comment in this way?

Emit signal on settings change in group instance

It would be good if changing settings would emit a signal that can be used to react to settings changes.

In practice, I am defining settings for a Bootstrap colour theme, and want to regenerate stylesheets from SCSS after the settings have been changed. I am listening to the post_save signal of the Setting model now, which is inefficient.

Too many titles

I don't want/need to group my settings (flat is better than nested ;-) ) so I've only got a single group. Unfortunately the settings page in the admin has a heck of a lot of titles that I can't get rid of

  1. Site settings (the global h1)
  2. "DJANGO_SITE" (my app_label)
  3. "Application settings" (hard-coded)

If I set verbose_name on the group it adds another title underneath 'Application settings' rather than replacing it as I would have expected.

So - bare minimum the page has 3 titles. Ideally I'd like one - the h1 is sufficient. I can understand the app_label as a subtitle if I had more than one app_label but even in this case shouldn't it be optional?

ForeignKey warning with Django 1.10

System check identified no issues (0 silenced).
venv/lib/python3.5/site-packages/dbsettings/migrations/0001_initial.py:24:

RemovedInDjango20Warning: on_delete will be a required arg for ForeignKey in Django 2.0. Set it to models.CASCADE on models and in existing migrations if you want to maintain the current default behavior.
See https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.on_delete

] + ([('site', models.ForeignKey(to='sites.Site'))] if USE_SITES else [])

migrate or syncdb warning on dbsettings 0.9.3 and django 1.8.5?

On Django 1.8.5 and dbsettings 0.9.3, I can't create the fields.

For testing purposes I used this on my core.models.py

import dbsettings

class EmailOptions(dbsettings.Group):
    enabled = dbsettings.BooleanValue('whether to send emails or not')
    sender = dbsettings.StringValue('address to send emails from')
    subject = dbsettings.StringValue(default='SiteMail')

email = EmailOptions()       

After migrate, the table exists in the database but the values are never populated.
If I try to run migrate again or syncdb it raises an error:

The following content types are stale and need to be deleted:

    core | 

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'.

    Type 'yes' to continue, or 'no' to cancel: 

Either yes or no does nothing.

# select * from dbsettings_setting;
 id | module_name | class_name | attribute_name | value 
----+-------------+------------+----------------+-------
(0 rows)

Does anyone with django 1.8.5 have the save issue?

empty settings page after port

Hi,

I've been using dbsettings for quite some time, without any issue.

Recently we migrated our django app from python2/django1.11/suit to python3/django2.2/baton, and as an unexpected consequence, the dbsettings page is empty (pg is the name of the app):

Screen Shot 2020-06-22 at 3 49 47 AM

Apart from that, everything is functional. The dbsettings are accessible programmatically, and appropriately set, etc. It's really just the web interface to the dbsettings that is not working.

Any idea on what could be happening here??

Thanks!

Choice settings?

Hi,

Many of the settings in my current application (which does not use db settings yet!) are choices, eg:

FOO_CHOICES = (
    (u'OK', 'Approved'),
    (u'FAIL', 'Rejected'),
)

I don't see any choice-like setting in the value types. I understand that this might be because there is no obvious mapping to primitive values to put in the database.

The most crazy solution I can think of is to use a String setting with the content "((u'OK', 'Approved'), (u'FAIL', 'Rejected'),)" and then use eval to get the dictionary back. This is of course a bit violent and risky.

What would you recommend?

Thanks!

Increasing max_length of 'value' field

I'm using dbsettings to allow editing of HTML snippets that aren't specific to a particular page on the site.

A max_length of 255 is too short in many cases. I'm pondering the best way to handle this:

  1. Increase it for everyone. Is there a problem with this? As far as I'm aware - varchar fields only use the amount of storage they need but there is probably some performance or storage cost. My hunch is that it's negligible.
  2. Make the base Setting class abstract or swappable so users can tweak this. Seems a bit complicated unless there are other benefits you can see growing from this.
  3. Stick to what we've got and I'll fork it. Obviously my least favourite option as I'll need to track changes forever more. However - if you feel that it's especially weird of me to want values longer than 255 then I can see this option might be your preferred one.

using choices returns TypeError

I am trying to set the choices for a StringValue using the tuple of tuples as specified in django docs and am getting the error

TypeError: The type of CHOICES (tuple) is not a valid Value.

when doing a simple

CHOICES = (
        ('A', 'Thing A'),
        ('B', 'Thing B'),
    )

    test = dbsettings.StringValue(default='A', 
        choices=CHOICES)

Release new version on PyPI

Hi,
could you please release a new version on PyPI? The last version is 1.2.0 which is not compatible with Django 4.0. The changes to make it compatible are already on the master branch but sadly there is no version with them on PyPI.

App level settings not showing up anywhere.

  1. I installed the package into my environment
    pip install django-dbsettings
  2. I installed it into my settings
    `INSTALLED_APPS = [ ..., 'dbsettings']
  3. I configured the urlpattern for it
    path('admin/settings/', include('dbsettings.urls')), and tried path('settings/', include('dbsettings.urls')),
  4. In my models for my base app I have
# base/models.py

import dbsettings
...

class SiteSettings(dbsettings.Group):
    company_name = dbsettings.StringValue(default='Your Company Name')

site_settings = SiteSettings()

class MyModel(models.Model): 
    name = models.CharField(default='blah')

    def __str__(self):
        return self.name
  1. I did python manage.py migrate and the settings model registered.

And when I go to 127.0.0.1:8000/admin/settings/ or 127.0.0.1:8000/settings/, it just says the app name Base with the button Save with no items or settings to select.
image

Did I do something wrong?

If so (or not), can you please make it clearer in the instructions how to set app level settings?

DBSETTINGS_VALUE_LENGTH and migrations

In #17 I suggested using a TextField instead of a CharField for the settings field as the 255 char limit was a major limitation for many use cases.

You suggested instead adding a setting DBSETTINGS_VALUE_LENGTH to allow overriding of max_length.

I've just switched back to using this branch instead of my own fork and I spotted the fact that you use this setting inside the initial migration. Therefore this setting gets 'baked in' to the schema on the first migration. Changing is after that is a bit tricky - anyone doing so would have to set their own MIGRATION_MODULES setting: https://docs.djangoproject.com/en/1.8/ref/settings/#std:setting-MIGRATION_MODULES and become responsible for managing any future migrations.

At the very least this should be documented. People should choose a high-enough DBSETTINGS_VALUE_LENGTH before their first migration to reduce the likelihood of needing a custom MIGRATION_MODULES setting.

ImageValue: hashlib.md5 problems with Python 3.x

Hello,

I'm using Python 3.5 and Dango 1.9.

When I use ImageValue i'm getting the following error:

File "/home/user/virtualenv3.5/lib/python3.5/site-packages/dbsettings/values.py", line 306, in get_db_prep_save
    hashed_name = md5(six.text_type(time.time())).hexdigest() + value.name[-4:]
TypeError: Unicode-objects must be encoded before hashing

So you can see that the problem is on file dbsettings/values.py on line 306:

hashed_name = md5(six.text_type(time.time())).hexdigest() + value.name[-4:]

CAUSE

Since Python 3.0, for hashlib.md5 you need to pass bytes instead of str. You can confirm that in Python documentation:

https://docs.python.org/2.7/library/hashlib.html
https://docs.python.org/3.0/library/hashlib.html
https://docs.python.org/3.1/library/hashlib.html
...
https://docs.python.org/3.5/library/hashlib.html

RESOLUTION

Replace line 306 with the following code:

        import sys
        PYTHON_VERSION = sys.version_info
        value_to_hash = six.text_type(time.time())
        if PYTHON_VERSION >= (3, 0):
            value_to_hash = value_to_hash.encode()
        hashed_name = md5(value_to_hash).hexdigest() + value.name[-4:]

Thanks for your great work.

Remove previous image file when ImageValue change

Hi,

When the value of an ImageValue field changes, the previous image file is preserved in the file system.

It would be a good improvement to remove the previous file just after the new one is uploaded.

To do this I inserted the following code in line 320 of value.py:

        # Remove previous image file:
        current_value = self.__get__(value)
        current_filename = pjoin(settings.MEDIA_ROOT, current_value)
        if os.path.isfile(current_filename):
            os.remove(current_filename)

As a reference, the complete get_db_prep_save() function must look like this:

    def get_db_prep_save(self, value):
        "Returns a value suitable for storage into a CharField"
        if not value:
            return None

        hashed_name = md5(six.text_type(time.time()).encode()).hexdigest() + value.name[-4:]
        image_path = pjoin(self._upload_to, hashed_name)
        dest_name = pjoin(settings.MEDIA_ROOT, image_path)
        directory = pjoin(settings.MEDIA_ROOT, self._upload_to)

        if not os.path.exists(directory):
            os.makedirs(directory)
        with open(dest_name, 'wb+') as dest_file:
            for chunk in value.chunks():
                dest_file.write(chunk)

        # Remove previous image file:
        current_value = self.__get__(value)
        current_filename = pjoin(settings.MEDIA_ROOT, current_value)
        if os.path.isfile(current_filename):
            os.remove(current_filename)

        return six.text_type(image_path)

I tested this on Django 1.9 With Python 3.5 and as you can see the code will work on any version of Python.

non-required integer value raises error

Hi,

I have a dbsettings.PositiveIntegerValue(required=False)

In the settings edition page, it is impossible to save changes if the above setting is left untouched. It raises: int() argument must be a string or a number, not 'NoneType'

Is this a bug or am I missing something?

Thanks!

Better document usage

Hello! I have some bits of config that can change by environment but are not secrets, and need to be editable without code changes and deployment. On a previous project, we had a custom model (with a small number of fields for our config buts) linked to sites.Site that did the job well. I thought dbsettings could be an easier and more powerful way (thanks to the included types) to do this, but it seems like I’m misunderstanding something in the docs.

After defining a class (with two settings without default values) and running migrations, I don’t see anything in the edit view. Is it mandatory to reference the settings from another model? Or is there some code I’m missing to make my settings appear in the admin?

Invalid ContentType after running "migrate" command

When using django 1.7 migrations i get the following message:

> ./manage migrate
...
The following content types are stale and need to be deleted:
    core |

core - is my app where i use django-dbsettings.

I found this closed ticket in django bugtracker: https://code.djangoproject.com/ticket/23519
Here is code which creates content-type with empty model: https://github.com/zlorf/django-dbsettings/blob/master/dbsettings/management.py#L13

    ct, created = ContentType.objects.get_or_create(model='', app_label=appname,
                                                    defaults={'name': appname})

To fix this we need to tell django "we need this model, so don't touch this" or find another way to implement app-level permissions.

MultipleObjectsReturned at /settings/

There seems to be a huge issue, the settings view is making objects over and over instead of modifying the existing Settings models.

Then at this point in loading.py I get the multiple objects error.

storage = Setting.objects.get(
                module_name=module_name,
                class_name=class_name,
                            attribute_name=attribute_name, ...
            )

I haven't been able to directly find the place where the Settings objects are created, so I have no idea why its making the same object twice.

Can't syncdb

Using python-2.7 and Django-1.7 and django-dbsettings-0.7

I have created options.py in my app with next content

import dbsettings

class GeneralOptions(dbsettings.Group):
    network = dbsettings.StringValue(
        'management network',
        default='192.168.200.0/24',
        help_text='Management network (where cluster nodes live)')


general = GeneralOptions('GeneralOptions')

Then I import whole module in my app's models.py, but when I do ./manage.py syncdb I get error

CommandError: System check identified some issues:

ERRORS:
dbsettings.Setting.site: (fields.E300) Field defines a relation with model 'Site', which is either not installed, or is abstract.

What I do wrong?

Django 3.0 support aka Drop Python 2 support

Django 3.0 removes the django.utils.six module and advises to move to the standard six library.

With Python 2 being end of life, I'd instead suggest dropping Python 2 compatibility altogether.

hashlib.md5 problems in Python 3.5 with ImageValue

Hello,

The fix applied for Issue #28 is raising a new error on Python 3.5:

hashed_name = md5(six.binary_type(time.time())).hexdigest() + value.name[-4:]
TypeError: 'float' object is not iterable

Current Line 309:

        hashed_name = md5(six.binary_type(time.time())).hexdigest() + value.name[-4:]

SOLUTION

Please go back using function six.text_type() and encode the result so it is compatible with Python 3.0+.

Line 309 must be like this:

        hashed_name = md5(six.text_type(time.time()).encode()).hexdigest() + value.name[-4:]

It was tested on Python 2.7 and Python 3.5 and works great!

Regards.

Django version compatibility?

The PyPI page and the README don't mention which Django versions the latest release of django-dbsettings is compatible with.

Migrations not included in PyPI download

Maybe I'm missing something obvious, but I can see the 0001_initial migration in the repo, but that directory appears to be missing from the tar.gz on PyPI

$ wget https://pypi.python.org/packages/source/d/django-dbsettings/django-dbsettings-0.8.1.tar.gz
$ tar -ztvf django-dbsettings-0.8.1.tar.gz | grep -c migrations
0

Is there some packaging issue? Thanks!

Extra files in PyPI archive

There are some unnecessary items in the package published on PyPI. Namely, an app and a cbv package:

$ tar tf django-dbsettings-0.7.tar.gz | sort
django-dbsettings-0.7/
django-dbsettings-0.7/app/
django-dbsettings-0.7/app/admin.py
django-dbsettings-0.7/app/__init__.py
django-dbsettings-0.7/app/models.py
django-dbsettings-0.7/AUTHORS
django-dbsettings-0.7/cbv/
django-dbsettings-0.7/cbv/__init__.py
django-dbsettings-0.7/cbv/settings.py
django-dbsettings-0.7/cbv/urls.py
django-dbsettings-0.7/cbv/wsgi.py
django-dbsettings-0.7/dbsettings/
django-dbsettings-0.7/dbsettings/forms.py
django-dbsettings-0.7/dbsettings/group.py
django-dbsettings-0.7/dbsettings/__init__.py
django-dbsettings-0.7/dbsettings/loading.py
django-dbsettings-0.7/dbsettings/locale/
django-dbsettings-0.7/dbsettings/locale/pl/
django-dbsettings-0.7/dbsettings/locale/pl/LC_MESSAGES/
django-dbsettings-0.7/dbsettings/locale/pl/LC_MESSAGES/django.mo
django-dbsettings-0.7/dbsettings/locale/pl/LC_MESSAGES/django.po
django-dbsettings-0.7/dbsettings/management.py
django-dbsettings-0.7/dbsettings/models.py
django-dbsettings-0.7/dbsettings/templates/
django-dbsettings-0.7/dbsettings/templates/dbsettings/
django-dbsettings-0.7/dbsettings/templates/dbsettings/app_settings.html
django-dbsettings-0.7/dbsettings/templates/dbsettings/site_settings.html
django-dbsettings-0.7/dbsettings/templates/dbsettings/values.html
django-dbsettings-0.7/dbsettings/tests/
django-dbsettings-0.7/dbsettings/tests/__init__.py
django-dbsettings-0.7/dbsettings/tests/tests.py
django-dbsettings-0.7/dbsettings/tests/test_urls.py
django-dbsettings-0.7/dbsettings/urls.py
django-dbsettings-0.7/dbsettings/utils.py
django-dbsettings-0.7/dbsettings/values.py
django-dbsettings-0.7/dbsettings/views.py
django-dbsettings-0.7/django_dbsettings.egg-info/
django-dbsettings-0.7/django_dbsettings.egg-info/dependency_links.txt
django-dbsettings-0.7/django_dbsettings.egg-info/PKG-INFO
django-dbsettings-0.7/django_dbsettings.egg-info/SOURCES.txt
django-dbsettings-0.7/django_dbsettings.egg-info/top_level.txt
django-dbsettings-0.7/LICENSE
django-dbsettings-0.7/MANIFEST.in
django-dbsettings-0.7/PKG-INFO
django-dbsettings-0.7/README.rst
django-dbsettings-0.7/runtests.py
django-dbsettings-0.7/setup.cfg
django-dbsettings-0.7/setup.py

These get installed to site-packages when using python setup.py install.

dbsettings forcing to use sites framework

I'm using Python 3.5 and Django 1.9.

I just created test project, added 'dbsettings' in INSTALLED_APPS on settings.py and ran the dev server with python manage.py runserver.

I'm getting the following error:

  File "/home/user/virtualenv3.5/lib/python3.5/site-packages/dbsettings/models.py", line 2, in <module>
    from django.contrib.sites.models import Site
  File "/home/user/virtualenv3.5/lib/python3.5/site-packages/django/contrib/sites/models.py", line 83, in <module>
    class Site(models.Model):
  File "/home/user/virtualenv3.5/lib/python3.5/site-packages/django/db/models/base.py", line 103, in __new__
    "application was loaded. " % (module, name))
RuntimeError: Model class django.contrib.sites.models.Site doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. 

But, if I add 'django.contrib.sites' in INSTALLED_APPS everything works OK.

The issue is that I do not need sites framework. I tried setting the variable DBSETTINGS_USE_SITES = False in settings.py and it did not help (the same error message).

I checked the source code of the file dbsettings/models.py and I believe I detected the problem: django.contrib.sites.models.Site must be imported only if using sites framework (USE_SITES). So the file dbsettings/models.py must be updated like this:

from django.db import models
from dbsettings.settings import USE_SITES, VALUE_LENGTH

if USE_SITES:
    from django.contrib.sites.models import Site

    class SiteSettingManager(models.Manager):
        def get_queryset(self):
            sup = super(SiteSettingManager, self)
            qs = sup.get_queryset() if hasattr(sup, 'get_queryset') else sup.get_query_set()
            return qs.filter(site=Site.objects.get_current())
        get_query_set = get_queryset


class Setting(models.Model):
    module_name = models.CharField(max_length=255)
    class_name = models.CharField(max_length=255, blank=True)
    attribute_name = models.CharField(max_length=255)
    value = models.CharField(max_length=VALUE_LENGTH, blank=True)

    if USE_SITES:
        site = models.ForeignKey(Site)
        objects = SiteSettingManager()

        def save(self, *args, **kwargs):
            self.site = Site.objects.get_current()
            return super(Setting, self).save(*args, **kwargs)

    def __bool__(self):
        return self.pk is not None

Please confirm if this change is valid and apply it to the repo.

Thanks for your work.

django-dbsettings prevents django.contrib.sites from being installed.

The error:

django.db.migrations.exceptions.InconsistentMigrationHistory: Migration dbsettings.0001_initial is applied before its dependency sites.0001_initial on database 'default'.

Instead of making sites mandatory, there is a migration that adapts itself to the app. Now I have to use a custom MIGRATIONS_MODULES setting with a sanitized migration in order to bypass this. I think that it would have been more consistent to make django.contrib.sites mandatory.

settings/ - superuser does not have permissions to edit

Django version 3.2.5
Logged in to admin as Superuser but seeing this message when accessing from localhost:8000/settings/

"You don't have permission to edit values."

I can visit admin and am logged in.
If I comment the staff-required decorator in views.py the issue remains

Creating an 'official' project fork?

Hi, I'm using your fork as it seemed to be the most active and stable.

Would you be interested in making it the 'official' version? Here's what I propose:

  1. Create a Github group called django-dbsettings so the project url would be: https://github.com/django-dbsettings/django-dbsettings. You would obvious be the main member but you could invite anyone else with an active fork to join if you preferred to share the burden
  2. Asking sciyoshi to update his README to point at the new project and also assign you as the manager on PyPi

I've done this recently with django-backup and django-admin-tools. I think it's the best way forward when the primary repo for a project is no longer being updated.

Alternatively - you can skip the Github group part if you prefer. I just think it's a nicer long-term strategy - if you cease to be interested in being the primary maintainer then it's much easier to pass the mantel.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.