Comments (8)
Hopefully resolved by #248
from django-ordered-model.
I also had problems with bottom()
not working when using with_respect_to
.
For my use case, the model is ordered with respect to active
. The problem came when I updated active
and then tried to call bottom()
. The problem is, the bottom()
method looks for the object with the highest order with the same value of active
, but it includes the instance it was called on.
My solution was to override the bottom
method and exclude the instance that it was called on. I took it straight from the source code of this package but added the exclude
clause.
Future people, feel free to use it, if it helps.
def bottom(self):
last = (
self.get_ordering_queryset()
.exclude(id=self.id)
.aggregate(Max('order'))
.get('order__max')
)
self.to(last + 1)
Every time this model saves. I check to see if active
got updated, and, if it did, I call the bottom()
method. This keeps the ordering from breaking when active
changes.
def save(self, *args, **kwargs):
old_active_value = MyModel.objects.get(id=self.id).active
super().save(*args, **kwargs)
if self.active is not old_active_value:
self.bottom()
from django-ordered-model.
Ah, you are right. It would awesome if there was a reshuffle()
method that checked for holes and closed them. However, in the meantime, here's my new iteration:
def bottom(self):
# Looks for the max value for 'order' in a given subset, or 0 if the subset is empty
last = (
self.get_ordering_queryset()
.exclude(id=self.id)
.aggregate(Max('order'))
.get('order__max')
) or 0
self.to(last + 1)
def save(self, *args, **kwargs):
# If this instance is being created for the first time, old instance won't exist
try:
old_instance = MyModel.objects.get(id=self.id)
except MyModel.DoesNotExist:
old_instance = None
super().save(*args, **kwargs)
if old_instance is not None and self.active is not old_instance.active:
# If active changed, the instance is switching to a different subset,
# when need to send that instance to the bottom of that subset.
self.bottom()
try:
# If there was an object after the instance in the OLD subset, move it to the
# instance's old spot.
MyModel.objects
.get(active=old_instance.active, order=old_instance.order + 1)
.to(old_instance.order)
except MyModel.DoesNotExist:
pass
from django-ordered-model.
@Hafnernuss There is a bug in your adaption, it doesn't save when updating an existing instance when the order_with_respect_to
field is unchanged.
A corrected version that is also adapted to work with both single and multiple order_with_respect_to
fields:
def save(self, *args, **kwargs):
if getattr(self, 'id') is not None:
# This is an existing object, so we need to check if any order_with_respect_to field has changed.
if isinstance(self.order_with_respect_to, str): # Single order_with_respect_to field.
order_with_respect_to_fields = [self.order_with_respect_to]
else: # Multiple order_with_respect_to fields.
order_with_respect_to_fields = self.order_with_respect_to
for field in order_with_respect_to_fields:
current_respect_field = getattr(self, field)
old_instance = self.__class__.objects.get(id=getattr(self, 'id'))
old_respect_field = getattr(old_instance, field)
if old_respect_field != current_respect_field:
old_instance.bottom()
setattr(self, self.order_field_name, None)
break
super().save(*args, **kwargs)
from django-ordered-model.
@timhaley94 This looks like most of the solution, it seems that it could leave a "hole" in the original order_with_respect sequence though. I like the technique to load the old value of the in the save() method and check for changes. It can fairly easily be made model-agnostic by using getattr()
from django-ordered-model.
It would be really great to get this into a PR and add some tests for this change.
from django-ordered-model.
I may have SOLVED this problem !!!!!
Problem
I want to implememt a category tree,
the original structure is like:
- c1 order:0
- c2 order:1
- c3 order:2
- c3-1 order:0
- c3-2 order:1
- c3-3 order:2
when I switch c3-2's parent from c3 to None(parent_category==None for root category),
this is what I want:
- c1 order:0
- c2 order:1
- c3 order:2
- c3-1 order:0
- c3-3 order:1
- c3-2 order:3
this is the actual result:
- c1 order:0
- c2 order:1
- c3-2 order:1
- c3 order:2
- c3-1 order:0
- c3-3 order:2
Solution
extend OrderedModelBase and override save() method:
from django.db import models
from ordered_model.models import OrderedModelBase
class BaseOrderedModel(OrderedModelBase):
index = models.PositiveIntegerField(db_index=True, editable=False, verbose_name='index')
order_field_name = "index"
class Meta:
abstract = True
ordering = ("index", )
def save(self, *args, **kwargs):
current_respect_field = getattr(self, self.order_with_respect_to)
if getattr(self, 'id') is not None:
# when update existing instance
old_instance = self.__class__.objects.get(id=getattr(self, 'id'))
old_respect_field = getattr(old_instance, self.order_with_respect_to)
if old_respect_field != current_respect_field:
# if order_with_respect_to field has changed
# 1. set instance to bottom of old subset before save
old_instance.bottom()
# 2. set self.[order_field_name] to None and save instance
setattr(self, self.order_field_name, None)
super().save(*args, **kwargs)
else:
# if order_with_respect_to field hasn't changed
super().save(*args, **kwargs)
else:
# when create new instance
super().save(*args, **kwargs)
Example
Let's define a Category
model which has a ForeignKey field parent_category
pointing to itself, and also with order_with_respect_to = 'parent_category'
:
class Category(BaseOrderedModel):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=50, verbose_name="名称")
parent_category = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='parent category')
order_with_respect_to = 'parent_category'
class Meta(BaseOrderedModel.Meta):
ordering = ('parent_category__id', 'index', )
def __str__(self):
return self.name
and according to my testing, everything works fine.
from django-ordered-model.
@duwangthefirst good idea, albeit only working if you have a single order_with_respect_to
field. I can confirm that this seems to work and is a probably an easy workaround until pull requests such as #272 are merged.
I made an easy adaption to support multiple fields:
def save(self, *args, **kwargs):
if getattr(self, 'id') is not None:
for field in self.order_with_respect_to:
current_respect_field = getattr(self, field)
# when update existing instance
old_instance = self.__class__.objects.get(id=getattr(self, 'id'))
old_respect_field = getattr(old_instance, field)
if old_respect_field != current_respect_field:
old_instance.bottom()
setattr(self, self.order_field_name, None)
super().save(*args, **kwargs)
break
else:
# when create new instance
super().save(*args, **kwargs)
from django-ordered-model.
Related Issues (20)
- foo.up(count=1) or foo.down(count=1) HOT 1
- Admin interface `move-up` action sometimes skips numbers HOT 9
- Django `ManyToMany` fields dont order results by through model's `Model.Meta.ordering` HOT 14
- Increase in queries number HOT 6
- 3.7.x causes error on Django startup HOT 4
- Additional checks for Admin
- If order_with_respect_to is a foreign key of a foreign key it generates aditional queries. HOT 4
- OrderWithRespectToTestsManyToMany.test_down defined twice HOT 1
- Upgrading from 3.6 to 3.7.x greatly decreased performance or raising a RecursionError in some situations HOT 3
- order_with_respect_to with non-foreign keys HOT 16
- Generalize `order_with_respect_to` beyond FK fields HOT 3
- Out-of-order deletion produces duplicate ordering
- (ordered_model.E001) OrderedModelBase subclass needs Meta.ordering specified check is required as an Error? HOT 1
- Add time complexity information to docs HOT 1
- `OrderedManyToMany` does not respect Django query cache
- Order With Respect To with Non Foreign Key Fields HOT 1
- `post_delete` signal called for both parent and child models, causing orders to be incorrect
- Ordering with Floats HOT 1
- [QUESTION] New release of 3.7.5 or 3.8 at PyPI
- order_with_respect_to with non-foreign keys HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from django-ordered-model.