Django change ordering of items in a Postgres ArrayField - django

dm_scripts = ArrayField(models.TextField(blank=True, null=True), default=list)
I have a Django model, and I wanted to use the ArrayField to store a list of TextFiles. The order matters, and I've been asked to allow a user to reorder these. How would you do that?
I've googled and searched here, but found nothing. Even Django's official documentation doesn't mention this.

I know this question is old, but I was looking for an answer, found one and thought I'd post the solution in case it is useful to others
A Django Postgres ArrayField is basically a list (at least in your case), so once you access it like object.dm_scripts you can manipulate it like any other list. For example, if you need to sort something alphabetically, you can call:
ordered_array = sorted(object.dm_scripts)
ordered_array.save()
Afterwards the list items will stay in that order. You can call that and apply it anywhere necessary, in a view, a def save method or whatever.
After that it's up to your imagination. Implementation would really depend on the UX interaction. I think it would be easy to pass the list to a javascript front-end, have the user reorder them visually, then apply the new order to the list items (Or many surely more optimised methods).

Related

Is there a Django ManyToManyField with implied ownership?

Let's imagine I'm building a Django site "CartoonWiki" which allows users to write wiki articles (represented by the WikiArticle-model) as well as posting in a forum (represented by the ForumPost-model). Over time more features will be added to the site.
A WikiArticle has a number of FileUploads which should be deleted when the WikiArticle is deleted. By "deleted" I mean Django's .delete()-method.
However, the FileUpload-model is generic -- it's not specific to WikiArticle -- and contains generic file upload logic that e.g. removes the file from S3 when it's removed from the database. Other models like ForumPost will use the FileUpload-model as well.
I don't want to use GenericForeignKey nor multi-table inheritance for the reasons Luke Plant states in the blog post Avoid Django's GenericForeignKey. (However, if you can convince me that there really is no better way than the trade-offs GenericForeignKey make, I might be swayed and accept a convincing answer of that sort.)
Now, the most trivial way to do this is to have:
class FileUpload(models.Model):
article = models.ForeignKey('WikiArticle', null=True, blank=True, on_delete=models.CASCADE)
post = models.ForeignKey('ForumPost', null=True, blank=True, on_delete=models.CASCADE)
But that will have the FileUpload-model expand indefinitely with more fields -- and similar its underlying table will gain more and more columns as new models in the system start using FileUpload. This feels suboptimal both in terms of data-modeling, but also in terms of separation-of-concerns -- the FileUpload-model and table is being changed while no actual new functionality is being added to it.
My preference would really be to go the other way around:
class WikiArticle(models.Model):
uploads = models.ManyToManyField('FileUpload')
But this doesn't solve the deletion issue: If I .delete() a WikiArticle the corresponding FileUploads won't be deleted. I've tried various setups with through-models, but none seem to solve it. What I really need is a OneToMany-field -- a sort of reverse ForeignKey to indicate the ownership in the right direction without polluting the generic/reusable model.
Should FileUpload really instead be a field? Or perhaps an abstract model? (WikiArticleFileUpload, ForumPostFileUpload, and so on...).
I realize that a true ManyToManyField with implied ownership would no longer really be a ManyToManyField since the field implies sharing. E.g. a FileUpload could technically be referenced by multiple WikiArticles, so you could be removing FileUploads from other objects rather on top of the one you're deleting. The question still stands though -- it seems I need a OneToManyField to model this in a nice way.
You probably have a couple of options to solve your problem, but it also requires on the exact requirements of your application.
Using a GenericForeignKey in this situation is probably fine, escpecially due to the fact that you do not know how many other models will use your upload model. Of course as mentioned in the linked blog post eg. doing plain SQL queries might be harder but it's on you to decide if that's a problem for your use case.
Also using inheritance might be an option, so that all the referenced models inherit the relation to the upload model from a common ancestor. This might have a small impact performance-wise because you Django would need to join the tables of the models but the impact might still be not that big. On the other hand this approach might also have some advantages if eg. your articles and posts have other stuff in common as well and you could easily do stuff like "show all new posts and articles (together)".
If you handle deletion yourself as mentioned in the previous answer you can also add ManyToMany fields yourself but also consider that this method also has some disadvantages in common with using generic foreign keys (eg. a lot of stuff to join in the database...)
Probably it's fine that you just use a GenericForeignKey, especially if the number of models that use your "generic" model gets bigger (eg. more than 3-5). All in all this sounds pretty much like a use case GenericForeignKey was made for (imagine the uploads being something like "tags" belonging to the posts).
ManyToMany fields are symmetrical, even though you define them on one model with an (explicit or implicit) related_name on the other.
I can think of two methods to clean up while, or after, WikiArticles are deleted. The first is to periodically search for and delete "orphan" FileUploads. At its simplest, (assuming a related_name of articles)
deleted = FileUpload.objects.filter( articles__isnull=True).delete()
The other is to explicitly process the related articles during deleting of the article. It's straightforward to subclass the object's delete method, but this is not the only way to delete an object (bulk_delete, for example, bypasses this). Anyway,
def delete( self, *args, **kwargs):
article_pks = self.uploads.all().values_list('pk', Flat=True)
response = super().delete( *args, **kwargs)
FileUpload.objects.filter(
pk__in = article_pks, articles__isnull=True) .delete()
return response
(or even just execute the "periodically" code above, for every article-deletion, which will also tity after any deleted though othr channels)
Please thoroughly test this if you use it. Delete operations which don't do precisely what is wanted are the scariest sorts of bug!

Query on a Many to Many relationship using through in Django

I'm new in Django and I'm giving myself a big headhache trying to structure this query.
I have a BaseProfile connected with a OneToOne field to User.
I'm specializing the profile in CustomerProfile connected with a OneToOne field to BaseProfile.
A CustomerProfile has a ManyToMany relationship with other CustomerProfile (so itself) through a RelatedCustomer model.
In the RelatedCustomer I specify the from_customer and to_customer Foreign Keys.
Maybe with an image you can understand better.
My problem:
Given a user.id I need to know all the other user.id of the customers that he is connected to (so passing through from_customer and to_customer):
So basically, first I need to dig from User to RelatedCustomer using reverse lookup, take all the set, and then going back to know the user.id of each customer in the set.
EDIT2:
What I've reached so far:
# This gives me back a customer profile given a user.id (2)
cm = CustomerProfile.objects.get(base_profile__user=2)
# M2M lookup. Given one customer returns all the RelatedCustomer relations
# that he has as a part of the 'from' M2M
cm.from_photographer.all()
Chaining the previous two: given a user.id I obtain a queryset of CustomerRelated relations:
rel = CustomerProfile.objects.get(base_profile__user=2).from_photographer.all()
This gives me back something like:
[<CustomerRelated: from TestCustomer4 to TestCustomer2 >,
<CustomerRelated: from TestCustomer4 to TestCustomer3 >]
Where in this case the user having a user.id=2 is the TestCustomer4.
My question:
So far so good, but now having this set how can I get all the user.id of the to_customer?
That is, how do I get the user.id of TestCustomer2 and TestCustomer3?
Firstly, this is not how you query the database in django. Secondly (since you're learning), it would be good to point out that you can run dbshell to try out different things. And lastly, this kind of problem is described in the documentation.
I am telling you this, because as a beginner, I also felt that it was a little difficult to navigate through the whole thing. The best way to find things is just to use google, and add a django at the end.
I know how you feel, the documentation search sucks, right? Heh, I feel you, that is why you always search the way I described it. Once you get a hang of the documentation, you will feel that the documentation title page is a little more intuitive.
Okay, so now to the answer:
To access a ManyToMany, OneToOne or ForeignKey field, you need to use a __ commonly known as dunder.
So, this is how I would go about doing this. Please note that there are other ways, and potentially better ways of doing this:
thing_I_want = RelatedCustomer.objects.get(to_customer__id=2)
Note, however that if you wanted to get a list of customers you would use filter(). Here is an example (which uses number of purchases as an example):
things_I_want = RelatedCustomer.objects.filter(to_customer__no_of_purchases=16)
Also note that the great thing about filter is that you stack one filter on top of another. You can read more about these features in the documentation link I provide below.
That will get you what you want. Now, you might have more queries regarding this, and how it all works together. Not to fear, please click this documentation link to check it out.
EDIT
Seems like what you want to do can be done by django, but if you want to do it using sql, then that is possible too. For example, SomeModel.objects.raw("SQL_HERE"). The name of the tables are usually <app>_<model>.
However, what you are asking can also be done in django, using the ORM. But it will be tricky.
Ok, as usual whenever you get the answer it always look much more easier than what you were expecting.
I guess this worked for me:
User.objects.filter(base_profile__customer_profile__to_customer__in=
User.objects.get(id=2).base_profile.customer_profile.from_customer.all())
Many thanks to #Games Brainiac

What do I use instead of ForiegnKey relationship for multi-db relation

I need to provide my users a list of choices from a model which is stored in a separate legacy database. Foreign keys aren't supported in django multi-db setups. The ideal choice would be to use a foreign key, but since thats not possible I need to some up with something else.
So instead I created an IntegerField on the other database and tried using choices to get a list of available options.
class OtherDBTable(models.Model):
client = models.IntegerField(max_length=20, choices=Client.objects.values_list('id','name'))
the problem I'm having is that the choices seem to get populated once but never refreshed. How do I ensure that whenever the client list changes that those newest options area available to pick.
What I was really looking for was a way that I could simulate the behavior of a Foreign key field, at least as far as matching up ID's go.
There wasn't a clear way to do this, since it doesn't seem like you can actually specify an additional field when you instantiate a model (you can with forms, easily)
In any case to deal with the problem, since the database is MySQL based, I ended up creating views from the tables I needed in the other database.
To build on #Yuji's answer - if you do self.fields['field'].choices = whatever in the model's __init__, whatever can be any iterable.
This means you can inherit from iterable, and have that object interface to your legacy database, or you can use a generator function (in case you are unfamiliar, look up the yield keyword).
Citing a Django's manual:
Finally, note that choices can be any iterable object -- not necessarily a list or tuple. This lets you construct choices dynamically. But if you find yourself hacking choices to be dynamic, you're probably better off using a proper database table with a ForeignKey. choices is meant for static data that doesn't change much, if ever.
Why dont you want just export data from the legacy database and to import it into the new one? This could be done periodically, if the legacy database still in use.

Django + Haystack how to do this search

I'm new to Haystack and to the search world so I didn't know how to ask this question.
What I want to achieve is the following.
Having a search query like: one two
I would like to get returned any content like:
This one
one
two
two one
something one here
Is this possible with Haystack + solr/xapian?
Is also possible to have a relevance on the result? In the case where both words are hit, that would give more relevance to me.
I'm currently using SearchQuerySet in my view but can't achieve that.
Cheers
So you're basically looking for an OR type query right? By default haystack uses an AND operation for joining queries.
You can do this two ways:
Change HAYSTACK_DEFAULT_OPERATOR within your settings.py to be OR. This will obviously be a site-wide change.
Modify your SearchQuerySet form to use filter_or which will force the OR style lookup. So pass a new one into your form/view: SearchQuerySet.filter_or(**kwargs)
Apart from that, you could always join Django Q objects together but considering you have these options, those are probably your best bet.
For relevance, you should read the Best Practices page which goes into using search templates and making them be your way to show relevant content.
Hope that helps!

Creating an order in Django

Hi everyone I have a few questions about the django admin.
First the relevant details. I currently have Client, Printer, Cartridge, and Order models.
The Printer model has a ManyToManyField to the Cartridge model, which would allow you to select all the cartridges that can be used with that printer.
The Cliente has a ManyToManyField to the printers which they own.
1) I want to create an Order through the Django admin which lets your specify the Client, a dicount, and multiple cartridges through a ManyToManyField. This is getting kinda tricky because I have to do it through another table that specifies whether it's a new Cartridge or a refill.
2) I want the admin to filters the Cartridges to only show the ones that belong to the printers that they own.
3) Also I would like to have a field that holds the total price of their order, but it should calculate it based on how many cartridges they have added to the order. I don't know if this should be done by adding more of the same cartridge to the order or by having another field in the related table that specifies the quantity.
Can this be done in the admin or do I need to use a form? And if so how would I go about adding this to the admin? It seems difficult and probably something I will have to do in multiple parts since in order to filter the list of cartridges I have to know the client beforehand.
As far as I can see, no, it's not really possible. The development version has some methods for limiting foreign keys, but it doesn't seem to me that limiting based on the customer is possible, since it depends on separate foreign keys.
The best suggestion, if you're really bent on doing it in the admin form, would be to use Javascript to do it. You would still have to make AJAX calls to get lists of what printers customers had and what cartridges to show based on that, but it could be done. You would just specify the JS files to load with the Media class.
But I think that's more work than it's worth. The easiest way I would see to do it would be with Form Wizards. That way, you'd have a step to select the customer so on the next step you know what cartridges to show.
Hope that helps!
I've worked similar problems, and have come to the conclusion that in many cases like this, it's really better to write your own administration interface using forms than it is to try and shoehorn functionality into the admin which is not intended to be there.
As far as 3) goes, it depends on what your product base looks like. If you're likely to have customers ordering 50 identical widgets, you probably do want a quantity field. If customers are more likely to be ordering 2 widgets, one in red, one in blue, add each item separately to the manytomany field and group them in your order interface.