معرفی شرکت ها


django-behaviors-0.5.1


Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر

توضیحات

Common behaviors for Django Models, e.g. Timestamps, Publishing, Authoring/Editing and more.
ویژگی مقدار
سیستم عامل -
نام فایل django-behaviors-0.5.1
نام django-behaviors
نسخه کتابخانه 0.5.1
نگهدارنده []
ایمیل نگهدارنده []
نویسنده Ryan Castner
ایمیل نویسنده castner.rr@gmail.com
آدرس صفحه اصلی https://github.com/audiolion/django-behaviors
آدرس اینترنتی https://pypi.org/project/django-behaviors/
مجوز MIT
============================= Django Behaviors ============================= .. image:: https://badge.fury.io/py/django-behaviors.svg :target: https://badge.fury.io/py/django-behaviors .. image:: https://travis-ci.org/audiolion/django-behaviors.svg?branch=master :target: https://travis-ci.org/audiolion/django-behaviors .. image:: https://codecov.io/gh/audiolion/django-behaviors/branch/master/graph/badge.svg :target: https://codecov.io/gh/audiolion/django-behaviors Common behaviors for Django Models, e.g. Timestamps, Publishing, Authoring/Editing and more. Inspired by Kevin Stone's `Django Model Behaviors`_. Documentation ============= Quickstart ---------- Install Django Behaviors:: pip install django-behaviors # Or, if you are going to use the Slugged behaviour pip install django-behaviors[slugged] Add it to your `INSTALLED_APPS`: .. code-block:: python INSTALLED_APPS = ( ... 'behaviors.apps.BehaviorsConfig', ... ) Features -------- ``behaviors`` makes it easy to integrate common behaviors into your django models: - **Documented**, **tested**, and **easy to use** - **Timestamped** to add ``created`` and ``modified`` attributes to your models - **StoreDeleted** to add ``deleted`` attribute to your models, avoiding the record to be deleted and allow to restore it - **Authored** to add an ``author`` to your models - **Editored** to add an ``editor`` to your models - **Published** to add a ``publication_status`` (draft or published) to your models - **Released** to add a ``release_date`` to your models - **Slugged** to add a ``slug`` to your models (thanks @apirobot) (ensure you have `awesome-slugify` installed, see above) - Easily compose together multiple ``behaviors`` to get desired functionality (e.g. ``Authored`` and ``Editored``) - Custom ``QuerySet`` methods added as managers to your models to utilize the added fields - Easily compose together multiple ``queryset`` or ``manager`` to get desired functionality Table of Contents ----------------- - `Behaviors`_ - `Timestamped`_ - `StoreDeleted`_ - `Authored`_ - `Editored`_ - `Published`_ - `Released`_ - `Slugged`_ - `Mixing in with Custom Managers`_ - `Mixing Multiple Behaviors`_ Behaviors --------- Timestamped Behavior `````````````````````` The model adds a ``created`` and ``modified`` field to your model. .. code-block:: python class Timestamped(models.Model): """ An abstract behavior representing timestamping a model with``created`` and ``modified`` fields. """ created = models.DateTimeField(auto_now_add=True, db_index=True) modified = models.DateTimeField(null=True, blank=True, db_index=True) class Meta: abstract = True @property def changed(self): return True if self.modified else False def save(self, *args, **kwargs): if self.pk: self.modified = timezone.now() return super(Timestamped, self).save(*args, **kwargs) ``created`` is set on the next save and is set to the current UTC time. ``modified`` is set when the object already exists and is set to the current UTC time. ``MyModel.changed`` returns a boolean representing if the object has been updated after created (the ``modified`` field has been set). Here is an example of using the model, note you do not need to add ``models.Model`` because ``Timestamped`` already inherits it. .. code-block:: python # models.py from behaviors.behaviors import Authored, Editored, Timestamped, Published class MyModel(Timestamped): name = models.CharField(max_length=100) >>> m = MyModel.objects.create(name='dj') >>> m.created '2017-02-14 17:20:19.835517+00:00' >>> m.modified None >>> m.changed False >>> m.save() >>> m.modified '2017-02-14 17:20:46.836395+00:00' >>> m.changed True StoreDeleted Behavior `````````````````````` The model add a ``deleted`` field to your model and prevent record to be deleted and allow to restore it .. code-block:: python class StoreDeleted(models.Model): """ An abstract behavior representing store deleted a model with``deleted`` field, avoiding the model object to be deleted and allowing you to restore it. """ deleted = models.DateTimeField(null=True, blank=True) objects = StoreDeletedQuerySet.as_manager() class Meta: abstract = True @property def is_deleted(self): return self.deleted != None def delete(self, *args, **kwargs): if not self.pk: raise ObjectDoesNotExist('Object must be created before it can be deleted') self.deleted = timezone.now() return super(StoreDeleted, self).save(*args, **kwargs) def restore(self, *args, **kwargs): if not self.pk: raise ObjectDoesNotExist('Object must be created before it can be restored') self.deleted = None return super(StoreDeleted, self).save(*args, **kwargs) ``deleted`` is set when ``delete()`` method is called, with current UTC time. Here is an example of using the model, note you do not need to add ``models.Model`` because ``StoreDeleted`` already inherits it. .. code-block:: python # models.py from behaviors.behaviors import StoreDeleted class GreatModel(StoreDeleted): name = models.CharField(max_length=100) # Deleting model >>> gm = GreatModel.objects.create(name='Xtra') >>> gm.deleted None >>> gm.delete() >>> gm.deleted '2018-05-14 08:35:41.197661+00:00' # Restoring model >>> gm = GreatModel.objects.deleted(name='Xtra') >>> gm.deleted '2018-05-14 08:35:41.197661+00:00' >>> gm.restore() >>> gm.deleted None Authored Behavior `````````````````` The authored model adds an ``author`` attribute that is a foreign key to the ``settings.AUTH_USER_MODEL`` and adds manager methods through ``objects`` and ``authors``. .. code-block:: python class Authored(models.Model): """ An abstract behavior representing adding an author to a model based on the AUTH_USER_MODEL setting. """ author = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="%(app_label)s_%(class)s_author") objects = AuthoredQuerySet.as_manager() authors = AuthoredQuerySet.as_manager() class Meta: abstract = True Here is an example of using the behavior and its ``authored_by()`` manager method: .. code-block:: python # models.py from behaviors.behaviors import Authored class MyModel(Authored): name = models.CharField(max_length=100) >>> m = MyModel.objects.create(author=User.objects.get(pk=2), name='tj') >>> m.author <User: ...> >>> queryset = MyModel.objects.authored_by(User.objects.get(pk=2)) >>> queryset.count() 1 The author is a required field and must be provided on initial ``POST`` requests that create an object. A custom ``models.ModelForm`` is provided to automatically add the ``author`` on object creation: .. code-block:: python # forms.py from behaviors.forms import AuthoredModelForm from .models import MyModel class MyModelForm(AuthoredModelForm): class Meta: model = MyModel fields = ['name'] # views.py from django.views.generic.edit import CreateView from .forms import MyModelForm from .models import MyModel class MyModelCreateView(CreateView): model = MyModel form = MyModelForm # add request to form kwargs def get_form_kwargs(self): kwargs = super(MyModelCreateView, self).get_form_kwargs() kwargs['request'] = self.request return kwargs Now when the object is created the ``author`` will be added on the call to ``form.save()``. If you are using functional views or another view type you simply need to make sure you pass the request object along with the form. .. code-block:: python # views.py class MyModelView(View): template_name = "myapp/mymodel_form.html" def get(self, request, *args, **kwargs): context = { 'form': MyModelForm(), } return render(request, self.template_name, context=context) def post(self, request, *args, **kwargs): # pass in request object to the request keyword argument form = MyModelForm(self.request.POST, request=request) if form.is_valid(): form.save() return reverse(..) context = { 'form': form, } return render(request, self.template_name, context=context) If for some reason you don't want to mixin the ``AuthoredModelForm`` with your existing form you can just add the user like so: .. code-block:: python # ... if form.is_valid() obj = form.save(commit=False) obj.author = request.user obj.save() return reverse(..) # ... But it isn't recommended, the ``AuthoredModelForm`` is tested and doesn't reassign the author on every save. The ``related_name`` is set so that it will never create conflicts. Given the above example if you wanted to do a reverse foreign key lookup from the User model and ``MyModel`` was part of the ``blogs`` app it could be done like so: .. code-block:: python >>> user = User.objects.get(pk=2) >>> user.blogs_mymodel_author.all() [<MyModel: ...>] That would give a list of all ``MyModel`` objects that ``user`` has ``authored``. Authored QuerySet .................. The ``Authored`` behavior attaches a custom model manager to the default ``objects`` and to the ``authors`` variables on the model it is mixed into. If you haven't overrode the ``objects`` variable with a custom manager then you can use that, otherwise the ``authors`` variable is a fallback. To get all ``MyModel`` instances authored by people whose name starts with 'Jo' .. code-block:: python # case is insensitive so 'joe' or 'Joe' matches >>> MyModel.objects.authored_by('Jo') [<MyModel: ...>, <MyModel: ...>, ...] # or use the authors manager variable >>> MyModel.authors.authored_by('Jo') [<MyModel: ...>, <MyModel: ...>, ...] See `Mixing in with Custom Managers`_ for details on how to mix in this behavior with a custom manager you have that overrides the ``objects`` default manager. Editored Behavior `````````````````` The editored model adds an ``editor`` attribute that is a foreign key to the ``settings.AUTH_USER_MODEL`` and adds manager methods through ``objects`` and ``editors`` variables. .. code-block:: python class Editored(models.Model): """ An abstract behavior representing adding an editor to a model based on the AUTH_USER_MODEL setting. """ editor = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="%(app_label)s_%(class)s_editor", blank=True, null=True) objects = EditoredQuerySet.as_manager() editors = EditoredQuerySet.as_manager() class Meta: abstract = True The ``Editored`` model is similar to the ``Authored`` model except the foreign key is **not required**. Here is an example of its usage: .. code-block:: python # models.py from behaviors.behaviors import Editored class MyModel(Editored): name = models.CharField(max_length=100) >>> m = MyModel.objects.create(name='pj') >>> m.editor None >>> m.editor = User.objects.all()[0] >>> m.save() >>> queryset = MyModel.objects.edited_by(User.objects.all()[0]) >>> queryset.count() 1 By default the ``editor`` is blank and null, if a ``request`` object is supplied to the form it will assign a new editor and erase the previous editor (or the null editor). Instead of using the ``AuthoredModelForm`` use the ``EditoredModelForm`` as a mixin to your form. .. code-block:: python # forms.py from behaviors.forms import EditoredModelForm from .models import MyModel class MyModelForm(EditoredModelForm): class Meta: model = MyModel fields = ['name'] # views.py from django.views.generic.edit import CreateView, UpdateView from .forms import MyModelForm from .models import MyModel MyModelRequestFormMixin(object): # add request to form kwargs def get_form_kwargs(self): kwargs = super(MyModelCreateView, self).get_form_kwargs() kwargs['request'] = self.request return kwargs class MyModelCreateView(MyModelRequestFormMixin, CreateView): model = MyModel form = MyModelForm class MyModelUpdateView(MyModelRequestFormMixin, UpdateView): model = MyModel form = MyModelForm Now when the object is created or updated the ``editor`` will be updated on the call to ``form.save()``. If you are using functional views or another view type you simply need to make sure you pass the request object along with the form. .. code-block:: python # views.py class MyModelView(View): template_name = "myapp/mymodel_form.html" def get(self, request, *args, **kwargs): context = { 'form': MyModelForm(), } return render(request, self.template_name, context=context) def post(self, request, *args, **kwargs): # pass in request object to the request keyword argument form = MyModelForm(self.request.POST, request=request) if form.is_valid(): form.save() return reverse(..) context = { 'form': form, } return render(request, self.template_name, context=context) If for some reason you don't want to mixin the ``EditoredModelForm`` with your existing form you can just add the user like so: .. code-block:: python ... if form.is_valid() obj = form.save(commit=False) obj.editor = request.user obj.save() return reverse(..) ... But it isn't recommended, the ``EditoredModelForm`` is tested and doesn't cause errors if request.user is invalid. The ``related_name`` is set so that it will never create conflicts. Given the above example if you wanted to do a reverse foreign key lookup from the User model and ``MyModel`` was part of the ``blogs`` app it could be done like so: .. code-block:: python >>> user = User.objects.get(pk=2) >>> user.blogs_mymodel_editor.all() [<MyModel: ...>] That would give a list of all ``MyModel`` objects that ``user`` is an ``editor``. Editored QuerySet .................. The ``Editored`` behavior attaches a custom model manager to the default ``objects`` and to the ``editors`` variables on the model it is mixed into. If you haven't overrode the ``objects`` variable with a custom manager then you can use that, otherwise the ``editors`` variable is a fallback. To get all ``MyModel`` instances edited by people whose name starts with 'Jo' .. code-block:: python # case is insensitive so 'joe' or 'Joe' matches >>> MyModel.objects.edited_by('Jo') [<MyModel: ...>, <MyModel: ...>, ...] # or use the editors manager variable >>> MyModel.editors.edited_by('Jo') [<MyModel: ...>, <MyModel: ...>, ...] See `Mixing in with Custom Managers`_ for details on how to mix in this behavior with a custom manager you have that overrides the ``objects`` default manager. Published Behavior ```````````````````` The ``Published`` behavior adds a field ``publication_status`` to your model. The status has two states: 'Draft' or 'Published'. .. code-block:: python class Published(models.Model): """ An abstract behavior representing adding a publication status. A ``publication_status`` is set on the model with Draft or Published options. """ DRAFT = 'd' PUBLISHED = 'p' PUBLICATION_STATUS_CHOICES = ( (DRAFT, 'Draft'), (PUBLISHED, 'Published'), ) publication_status = models.CharField( "Publication Status", max_length=1, choices=PUBLICATION_STATUS_CHOICES, default=DRAFT) class Meta: abstract = True objects = PublishedQuerySet.as_manager() publications = PublishedQuerySet.as_manager() @property def draft(self): return self.publication_status == self.DRAFT @property def published(self): return self.publication_status == self.PUBLISHED The class offers two properties ``draft`` and ``published`` to know object state. The ``DRAFT`` and ``PUBLISHED`` class constants will be available from the class the ``Published`` behavior is mixed into. There is also a custom manager attached to ``objects`` and ``publications`` variables to get ``published()`` or ``draft()`` querysets. .. code-block:: python # models.py from behaviors.behaviors import Published class MyModel(Published): name = models.CharField(max_length=100) >>> m = MyModel.objects.create(name='cj') >>> m.publication_status u'd' >>> m.draft True >>> m.published False >>> m.get_publication_status_display() u'Draft' >>> MyModel.objects.published().count() 0 >>> MyModel.objects.draft().count() 1 >>> m.publication_status = MyModel.PUBLISHED >>> m.save() >>> m.publication_status u'p' >>> m.draft False >>> m.published True >>> m.get_publication_status_display() u'Published' >>> MyModel.objects.published().count() 1 >>> MyModel.PUBLISHED u'p' >>> MyModel.PUBLISHED == m.publication_status True The ``publication_status`` field defaults to ``Published.DRAFT`` when you make new models unless you supply the ``Published.PUBLISHED`` attribute to the ``publication_status`` field. .. code-block:: python MyModel.objects.create(name='Jim-bob Cooter', publication_status=MyModel.PUBLISHED) Published QuerySet ................... The ``Published`` behavior attaches to the default ``objects`` variable and the ``publications`` variable as a fallback if ``objects`` is overrode. .. code-block:: python # returns all MyModel.PUBLISHED MyModel.objects.published() MyModel.publications.published() # returns all MyModel.DRAFT MyModel.objects.draft() MyModel.publications.draft() Released Behavior `````````````````` The ``Released`` behavior adds a field ``release_date`` to your model. The field is **not_required**. The release date can be set with the ``release_on(datetime)`` method. .. code-block:: python class Released(models.Model): """ An abstract behavior representing a release_date for a model to indicate when it should be listed publically. """ release_date = models.DateTimeField(null=True, blank=True) class Meta: abstract = True objects = ReleasedQuerySet.as_manager() releases = ReleasedQuerySet.as_manager() def release_on(self, date=None): if not date: date = timezone.now() self.release_date = date self.save() @property def released(self): return self.release_date and self.release_date < timezone.now() There is a ``released`` property added which determines if the object has been released. There is a custom manager attached to ``objects`` and ``releases`` variables to filter querysets on their release date. Here is an example of using the behavior: .. code-block:: python # models.py from django.utils import timezone from datetime import timedelta from behaviors.behaviors import Released class MyModel(Released): name = models.CharField(max_length=100) >>> m = MyModel.objects.create(name='rj') >>> m.release_date None >>> MyModel.objects.no_release_date().count() 1 >>> m.release_on() >>> MyModel.objects.no_release_date().count() 0 >>> MyModel.objects.released().count() 1 >>> m.release_on(timezone.now() + timedelta(weeks=1)) >>> MyModel.objects.not_released().count() 1 >>> MyModel.objects.released().count() 0 The ``release_on`` method defaults to the current time so that the object is immediately released. You can also provide a date to the method to release on a certain date. ``release_on()`` just serves as a wrapper to setting and saving the date. You can always provide a ``release_date`` on object creation: .. code-block:: python MyModel.objects.create(name='Jim-bob Cooter', release_date=timezone.now()) Released QuerySet ................... The ``Released`` behavior attaches to the default ``objects`` variable and the ``releases`` variable as a fallback if ``objects`` is overrode. .. code-block:: python # returns all not released MyModel objects MyModel.objects.not_released() MyModel.releases.not_released() # returns all released MyModel objects MyModel.objects.released() MyModel.releases.released() # returns all null release date MyModel objects MyModel.objects.no_release_date() MyModel.releases.no_release_date() Slugged Behavior `````````````````` The ``Slugged`` behavior allows you to easily add a ``slug`` field to your model. The slug is generated on the first model creation or the next model save and is based on the ``slug_source`` attribute. **The** ``slug_source`` **property has no set default, you must add it to your model for the behavior to work.** .. code-block:: python class Slugged(models.Model): """ An abstract behavior representing adding a slug (by default, unique) to a model based on the slug_source property. """ slug = models.SlugField( max_length=255, unique=BehaviorsConfig.are_slug_unique(), blank=True) class Meta: abstract = True def save(self, *args, **kwargs): if not self.slug: self.slug = self.generate_unique_slug() \ if BehaviorsConfig.are_slug_unique() else self.get_slug() super(Slugged, self).save(*args, **kwargs) def get_slug(self): return slugify(getattr(self, "slug_source"), to_lower=True) def is_unique_slug(self, slug): qs = self.__class__.objects.filter(slug=slug) return not qs.exists() def generate_unique_slug(self): slug = self.get_slug() new_slug = slug iteration = 1 while not self.is_unique_slug(new_slug): new_slug = "%s-%d" % (slug, iteration) iteration += 1 return new_slug The ``slug`` uses the awesome-slugify package which will preserve unicode character slugs. By default, the ``slug`` must be unique and is guaranteed to be unique by the class appending a number ``-[0-9+]`` to the end of the slug if it is not unique. The ``unique`` field type `adds an index`_ to the ``slug`` field. Add the ``slug_source`` property to your class when mixing in the behavior. To allow non-unique slugs, add ``UNIQUE_SLUG_BEHAVIOR = False`` to your project's settings. .. code-block:: python # models.py from behaviors.behaviors import Slugged class MyModel(Slugged): name = models.CharField(max_length=100) # slug_source is required for the slug to be set @property def slug_source(self): return "prepended-text-for-fun-{}".format(self.name) # you can now use the slug for your get_absolute_url() method def get_absolute_url(self): return reverse('myapp:mymodel_detail', args=[self.slug]) >>> m = MyModel.objects.create(name='aj') >>> m.slug 'prepended-text-for-fun-aj' >>> m2 = MyModel.objects.create(name='aj') >>> m.slug 'prepended-text-for-fun-aj-1' >>> m.get_absolute_url() '/myapp/prepended-text-for-fun-aj/detail' Your ``slug_source`` attribute can be a mix of any of the model data available at the time of save, generally it is some ``name`` type of field. You could also hash the primary key and/or some other data as a ``slug_source``. By default, the ``slug`` is unique so it can be used to define the ``get_absolute_url()`` method on your model. Thanks to @apirobot for sending the PR for the ``Slugged`` behavior. Mixing in with Custom Managers ------------------------------ If you have a custom manager on your model already: .. code-block:: python # models.py from behaviors.behaviors import Authored, Editored, Published, Timestamped from django.db import models class MyModelCustomManager(models.Manager): def get_queryset(self): return super(MyModelCustomManager).get_queryset(self) def custom_manager_method(self): return self.get_queryset().filter(name='Jim-bob') class MyModel(Authored): name = models.CharField(max_length=100) # MyModel.objects.authored_by(..) won't work # MyModel.authors.authored_by(..) still will objects = MyModelCustomManager() Simply add ``AuthoredManager`` from ``behaviors.managers`` as a mixin to ``MyModelCustomManager`` so they can share the ``objects`` variable. .. code-block:: python # models.py from behaviors.behaviors import Authored, Editored, Published, Timestamped from behaviors.managers import AuthoredManager, EditoredManager, PublishedManager from django.db import models class MyModelCustomManager(AuthoredManager, models.Manager): def get_queryset(self): return super(MyModelCustomManager).get_queryset(self) def custom_manager_method(self): return self.get_queryset().filter(name='Jim-bob') class MyModel(Authored): name = models.CharField(max_length=100) # MyModel.objects.authored_by(..) now works objects = MyModelCustomManager() Similarly if you are using a custom QuerySet and calling its ``as_manager()`` method to attach it to ``objects`` you can import from ``behaviors.querysets`` and mix it in. .. code-block:: python # models.py from behaviors.behaviors import Authored, Editored, Published, Timestamped from behaviors.querysets import AuthoredQuerySet, EditoredQuerySet, PublishedQuerySet from django.db import models class MyModelCustomQuerySet(AuthoredQuerySet, models.QuerySet): def custom_queryset_method(self): return self.filter(name='Jim-bob') class MyModel(Authored): name = models.CharField(max_length=100) # MyModel.objects.authored_by(..) works objects = MyModelCustomQuerySet.as_manager() Mixing in Multiple Behaviors ---------------------------- Many times you will want multiple behaviors on a model. You can simply mix in multiple behaviors and, if you'd like to have all their custom ``QuerySet`` methods work on ``objects``, provide a custom manager with all the mixins. .. code-block:: python # models.py from behaviors.behaviors import Authored, Editored, Published, Timestamped from behaviors.querysets import AuthoredQuerySet, EditoredQuerySet, PublishedQuerySet from django.db import models class MyModelQuerySet(AuthoredQuerySet, EditoredQuerySet, PublishedQuerySet): pass class MyModel(Authored, Editored, Published, Timestamped): name = models.CharField(max_length=100) # MyModel.objects.authored_by(..) works # MyModel.objects.edited_by(..) works # MyModel.objects.published() works # MyModel.objects.draft() works objects = MyModelQuerySet.as_manager() # you can also chain queryset methods >>> u = User.objects.all()[0] >>> u2 = User.objects.all()[1] >>> m = MyModel.objects.create(author=u, editor=u2) >>> MyModel.objects.published().authored_by(u).count() 1 Running Tests ------------- Does the code actually work? :: source <YOURVIRTUALENV>/bin/activate (myenv) $ pip install tox (myenv) $ tox Credits ------- Tools used in rendering this package: * Cookiecutter_ * `cookiecutter-djangopackage`_ .. _Cookiecutter: https://github.com/audreyr/cookiecutter .. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage .. _`Timestamped`: #timestamped-behavior .. _`StoreDeleted`: #storedeleted-behavior .. _`Authored`: #authored-behavior .. _`Editored`: #editored-behavior .. _`Published`: #published-behavior .. _`Released`: #released-behavior .. _`Slugged`: #slugged-behavior .. _`settings.AUTH_USER_MODEL`: https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-AUTH_USER_MODEL .. _`Mixing in with Custom Managers`: #mixing-in-with-custom-managers .. _`Mixing Multiple Behaviors`: #mixing-in-multiple-behaviors .. _`Django Model Behaviors`: http://blog.kevinastone.com/django-model-behaviors.html .. _`adds an index`: https://docs.djangoproject.com/en/dev/ref/models/fields/#unique History ======= 0.5.1 (2020-09-19) ------------------ * Feature: ``Slugged`` behavior now supports non-unique slugs (thanks @vonmaster) 0.5.0 (2019-07-01) ------------------ * Drop Django 2.0 support and Python 3.4 support as they have reached EOL * Add Django 2.1, 2.2 and Python 3.7 support * Feature: Add db_index to ``Timestamped`` behavior's ``modified`` field (thanks @kazqvaizer) 0.4.1 (2018-05-17) ------------------ * Feature: Add ``StoreDeleted`` behavior to soft delete models (thanks @abekroenem) 0.4.0 (2018-01-16) ------------------ * Update setup.py classifiers 0.3.1 (2018-01-04) ------------------ * Add Django 2.0 and Python 3.6 Support 0.3.0 (2017-03-11) ------------------ * Add ``Slugged`` behavior adding a slug to models * Update documentation 0.2.0 (2017-02-14) ------------------ * Add ``Released`` behavior for a release date on models * Update documentation 0.1.7 (2017-02-14) ------------------ * Remove an unused import * Integrate with Lintly 0.1.6 (2017-02-14) ------------------ * Drop python3.3 support for Django 1.8 because 1.8 no longer supports it 0.1.5 (2017-02-14) ------------------ * Fix import error for py2.7 builds 0.1.4 (2017-02-14) ------------------ * Fix Syntax Error 0.1.3 (2017-02-14) ------------------ * Fixed Circular Import 0.1.2 (2017-02-13) ------------------ * Travis CI Fixes 0.1.1 (2017-02-13) ------------------ * First release on PyPI * Flake8 adherence fixes 0.1.0 (2017-02-13) ------------------ * First push of project


نحوه نصب


نصب پکیج whl django-behaviors-0.5.1:

    pip install django-behaviors-0.5.1.whl


نصب پکیج tar.gz django-behaviors-0.5.1:

    pip install django-behaviors-0.5.1.tar.gz