How to give a model object an order (or index), which can be reassigned? - django

Say you have an ordered list. You order the list based on a model field called "index". So the first item has an index of 0, the second has an index of 1 and so on...
The index is unique to that model object.
How would you implement this?
I want to be able to create more instances of the model object, where the index is assigned to the next available index (add the object to the end of the list). And then be able to reorder the list so that if you change the index of an object, all the following object's indexes increase by one.

If you want an IntegerField that increments itself you can use the id. It's unique for that model and Django generates it automatic. And you can order by this using
inverse:
MyModel.objects.all().order_by('-id')
Normal order:
MyModel.objects.all().order_by('id')
If you just have a field that contains auto-increment-index don't need to create another one only if you can modify it, but if this index is unique you cannot edit it to prevent duplicates. Si I would use the id MyModel.id
Here you have the documentation for .order_by()

There is no field that does that automatically. Have you looked in to using signals for this? You could hook up a signal that detects an index change, and triggers a function that updates the index of every object whose current index is greater than the one being removed/changed.

You may ave to rethink your schema because if you change the index of your first element in the list which has lets say 1 million elements, you are gonna update 1 million objects! You may save for each object its left and right "neighbour" and create a method to get the list.

Related

Deleting Element inside a list DynamoDB

According to the link below in DynamoDB, you can only remove elements if you know the index of the array/list.
Add or remove an entry from a List type attribute in a DynamoDB table item
What I would like to know is how do you prevent index out of range error.
Or even worst...removing the wrong item because the index number changed.
This could happen when multiple users request removal of data at the sametime but their index number changed while the lambda function was working.
The only thing I can think of is
Request to see the table, thats call number 1 $$$
Confirm that the index is still valid
Then make the change that's call number 2 $$$
And this solution only reduces the time between check and update
It is still not a complete fix.
Is this an acceptable error ?
Using REMOVE to delete by index can lead to inconsistencies like you have mentioned.
If possible, it would be best to use a Set type so you can use the DELETE keyword where you can delete by value instead of index.
If not possible, you can add an extra layer of protection by including a ConditionExpression to the UpdateExpression to verify that the state of the list is the same as you expect. You could do something like verify the value at the index is the value you expect and/or verify that the list is the same size as you expect.

Ordered ManyToMany relation in Django with custom Field

In Django, I would like to have an ordered many-to-many-relation. Assume I have, say, the models OrderedList and Item, and I want to be able to insert Item()s into an OrderedList() at a specific index, I want to be able to retrieve the Item()s of an OrderedList() in their order and also to change the order of Item()s on an OrderedList
I already found Define an order for ManyToManyField with django and https://github.com/gregmuellegger/django-sortedm2m
Both the github repo and the accepted answer in the SO question are working with the same architecture: They create an additional integer field, say order_index, on the junction ("Through") table which represents the position of the Item() on the OrderedList().
Honestly, I do not like that too much. If I see this correctly, having the order stored on the junction table can create inefficiency when I want to reorder Item()s: Imagine, I want to change the position of an Item() on an OrderedList() which has n Item()s. This means O(n) database updates to reorganize the order indices.
I would like to avoid this. I think of an architecture where I have an ordinary many-to-many-relation and one additional column on the OrderedList table which holds a list of Item ids, say items_order. In this architecture, I need one database update and one list operation on items_order - which should be way faster, I guess.
I believe the best way for this is to create a custom model Field. The docs state how to create a custom model Field (https://docs.djangoproject.com/en/2.1/howto/custom-model-fields/) and I can create my items_order field like this. But I did not find how to make a custom Field which, besides creating the order_list, also creates the junction table and takes care of updating the items_order whenever a new related Item() is added or removed from the relation. I think, I should subclass the ManyToMany Field (https://docs.djangoproject.com/en/2.1/_modules/django/db/models/fields/related/#ManyToManyField). But I don't know how to do this, so could you give me some guidance here?

how to deal with virtual index in a database table in Django + PostgreSQL

Here is my current scenario:
Need to add a new field to an existing table that will be used for ordering QuerySet.
This field will be an integer between 1 and not a very high number, I expect less than 1000. The whole reasoning behind this field is to use it for visual ordering on the front-end, thus, index 1 would be the first element to be returned, index 2 second, etc...
This is how the field is defined in model:
priority = models.PositiveSmallIntegerField(verbose_name=_(u'Priority'),
default=0,
null=True)
I will need to re-arrange (reorder) the whole set of elements in this table if a new or existing element gets this field updated. So for instance, imagine I have 3 objects it this table:
Element A
priority 1
Element B
priority 2
Element C
priority 3
If I change Element C priority to 1 I should have:
Element C
priority 1
Element A
priority 2
Element B
priority 3
Since this is not a real db index ( and have empty values), I'm gonna have to query for all elements on database each time a new element is created / updated and change priority value for each record in table. Not really worried about performance since table will always be small BUT, I'm worried this way to proceed is not the way to go or simply it generates too much overhead.
Maybe there is simpler way to do this with plain SQL stuff? If I use an index though, I will get an error every time an existing priority is used, something I don't want either.
Any pointers?
To insert at 10th position all you need is a single sql query:
MyModel.objects.filter(priority__gte=10).update(priority=models.F('priority')+1)
Then you would need a similar one for deleting an element, and swapping two elements (or whatever your use case requires). It all should be doable in a similar manner with bulk update queries, no need to manually update entry by entry.
First, you can very well index this column, just don't enforce it to contains unique values. Such standard indexes can have nulls and duplicates... they are just used to locate the row(s) matching a criteria.
Second, updating each populated* row each time you insert/update a record should be looked at based on the expected update frequency. If each user is inserting several records each time they use the system and you have thousands of concurrent users, it might not be a good idea... whereas if you have a single user updating any number of rows once in a while, it is not so much an issue. On the same vein, you need to consider if other updates are occurring to the same rows or not. You don't want to lock all rows too often if they are to be updated/locked for updating other fields.
*: to be accurate, you wouldn't update all populated rows, but only the ones having a priority lower than the inserted one. (inserting a priority 999 would only decrease the priority of items with 999 and 1000)

Remove similar items on an object list

I am populating a std::list<item*> itemslist from a text file, but there are instances where the name variable is the same, in those instances I want to remove the duplicated item (the item with the same name, other information of the item is irrelevant) but get data from that object to add to the 'original' (the first instance of the item)
Because I want to be able to load multiple information, aka an array of sorts into 'items' and this is what I have so far come up with
I tried to loop through items, then loop again and check the first loop against the second loop, copy the information and add to the array of the first loop item but it just breaks.
Suggestions?
Similar to this: Removing duplicates in a vector of strings
Except I want to use the information found in any duplicate and add it to a std::list that the item object holds
So if I load this:
Set Normal DIfficulty|Sets your save game to normal difficulty|/JKSV/Saves/Fire_Emblem__Awakening/hack/Chapter0|0x0D|0x00
Set Normal DIfficulty|Sets your save game to normal difficulty|/JKSV/Saves/Fire_Emblem__Awakening/hack/Chapter0|0x0F|0x0A
Set Hard DIfficulty|Sets your save game to hard difficulty|/JKSV/Saves/Fire_Emblem__Awakening/hack/Chapter0|0x0D|0x01
Set Lunatic DIfficulty|Sets your save game to lunatic difficulty|/JKSV/Saves/Fire_Emblem__Awakening/hack/Chapter0|0x0D|0x02
Set Lunatic+ DIfficulty|Sets your save game to lunatic+ difficulty|/JKSV/Saves/Fire_Emblem__Awakening/hack/Chapter0|0x0D|0x03
It will only have 1 Set Normal Difficulty item but that item will hold the duplicates last 2 pieces of information (aka 0x0F|0x0A
If you want your std::list<T> to erase duplicate values you have to call unique member function. There are two overloads of it. First take 0 arguments and compares values using operator==. It is not your case cause your list contains pointers and you don't need to compare addresses but name members. So try a second one. It takes a binary predicate where you can compare members
std::list<item*> myList;
// populate your list..
// sort it before calling unique..
myList.sort([](const item *lhs,const item *rhs)->bool{
return lhs->name < rhs->name;
});
myList.unique([](const item *lhs,const item *rhs)->bool{
return lhs->name == rhs->name;
});
// now your list has no duplicates..
More info here.

Can I remove items from a ComboBox without changing the index of other items?

I have a CComboBox control with several items and I need to remove some of them, but the indexes of the remaining items should be preserved.
When the combo box is populated, the item data is set like so:
index = mycombo.AddString(temp);
mycombo.SetItemData(index, static_cast<DWORD>(count));
where count is a loop counter, and should be equal to index
Now I want to remove some of the items later, but I need the index of each remaining item to stay the same. Is CComboBox::DeleteString(UINT nIndex) what I should use? Its documentation says:
All items following nIndex now move down one position. For example, if a combo box contains two items, deleting the first item will cause the remaining item to now be in the first position. nIndex=0 for the item in the first position.
Is that talking about the physical location in the drop-down menu, or the index value associated with the item?
Is there another function that does what I need? Another solution altogether?
Is that talking about the physical location in the drop-down menu, or the index value associated with the item?
For a ComboBox (as well as ListBox, List Control, and probably many other such things) the location of an item on the control is directly tied to its index. The index is the location. Really, just think of it as if ComboBox was implemented internally using a simple std::vector. You can't remove an entry from a vector without affecting the indexes of all subsequent entries, and it is just the same with these controls.
However, the Item Data of an entry in a ComboBox (and other such controls) sticks with that entry no matter what index it is reassigned to.
Say you created two entries: the first at index 0 has text="A" and ItemData=0; while the second at index 1 has text="B" and ItemData=1. If you then remove that first entry, the second entry will shift down the claim the index and its ItemData will travel along. So you would be left with a single entry at index 0 having text="B" and ItemData=1.
In a combobox you have items which have a string and an integer value associated. Normally, you just see the string. Those items are referenced by an index, which just represents the location of each item in the list. If you remove an item, all the items below it are "relocated", so the index changes. The same happens in you insert an element anywhere between two items, or at the beginning.
The index always goes from 0 to (number_of_items-1), and there's nothing you can do about it.
That said, the item data always stays with the item, and it's this what you should look at when looking for a specific item. Not its index, and neither its string. Look at the item data. The index can change if you add, remove or resort the items. The strings will change if you localize the software. So use the data to properly identify each element.
You can take a look at http://www.flounder.com/combobox.htm, where you can find a better explanation, with some examples and code to do work with comboboxes more easily.
Adding or removing items does not change the number you passed to SetItemData(). GetItemData() returns the same number. You however need to pass the index of the item to DeleteString(). When lower numbered items were deleted before then the index will no longer match GetItemData(). If you lost track of the index of a specific item you want to delete then you need to iterate the items to find it back.