Imagine, I have News models with many text fields
class News(models.Model):
title = models.CharField(max_length=255)
subtitle = models.CharField(max_length=255, blank=True)
lead = models.TextField(max_length=4096)
content = models.TextField()
...
last_visited = models.DateTimeField()
Every time my News object outputs, I update last_visited field:
news.last_visited = datetime.datetime.now()
news.save()
This code makes Django override all model fields:
UPDATE news SET title='...', subtitle='...', last_visited = '...' WHERE id = '...';
Instead of just one:
UPDATE news SET last_visited = '...' WHERE id = '...';
I worried how bad it is and is it worth of thinking about.
Django documentation offers queryset update but it looks not very elegant:
def upd(obj, **kwargs):
obj.__class__._default_manager.filter(pk=obj.pk).update(**kwargs)
upd(news, last_visited=datetime.datetime.now())
I use mysql backend.
Using update but with a cleaner approach:
class News(models.Model):
def update_visited(self):
News.objects.filter(pk=self.pk).update(
last_visited=datetime.datetime.now())
I think using queryset update is good. It removes the possibility that you overwrite changes to other fields by accident.
I know you're worried that it looks inelegant, but you only have to use it once in your upd function, then use upd in your views.
Supposing you want to use this on more than one model (guessing this because you pass obj to your upd function) it would probably make sense to have some base class that implements the last_visited field and your News class inherits from this class... Then you could do the update just on your base class.... Another possibilty would be putting the last_visited information into a seperate model and referencing the News model either through a ForeignKey or a GenericForeignKey (in the case you want to keep a 'history' for different models).
Related
I have two models in consideration. RV_Offers and RV_Details. Each offer can have multiple details i.e. I have a foreignkey relationship field in RV_Details table.
Here is my view:
rv_offers_queryset = RV_Offers.objects.all().select_related('vendor').prefetch_related('details')
details_queryset = RV_Details.objects.all().select_related('rv_offer')
title = Subquery(details_queryset.filter(
rv_offer=OuterRef("id"),
).order_by("-created_at").values("original_title")[:1])
offers_queryset = rv_offers_queryset.annotate(
title=title).filter(django_query)
offers = RVOffersSerializer(offers_queryset, many=True).data
return Response({'result': offers}, status=HTTP_200_OK)
As can be seen, I am passing the offers queryset to the serializer.
Now, here is my serializer:
class RVOffersSerializer(serializers.ModelSerializer):
details = serializers.SerializerMethodField()
vendor = VendorSerializer()
def get_details(self, obj):
queryset = RV_Details.objects.all().select_related('rv_offer')
queryset = queryset.filter(rv_offer=obj.id).latest('created_at')
return RVDetailsSerializer(queryset).data
class Meta:
model = RV_Offers
fields = '__all__'
If you look at the get_details method, I am trying to fetch the latest detail that belongs to an offer. My problem is, even though I am using select_related to optimize the query, the results are still extremely slow, In fact, I am using django debug toolbar to inspect the query and apparently select_related seems to have no effect.
What am I doing wrong or how else can I approach this problem?
This is what I did to reduce the number of queries being hit on the db:
def get_details(self, obj):
details = obj.details.last()
return RVDetailsSerializer(details).data
I was able to reduce the number of queries from 45 to 4 using this.
This is because in the view, I had already used select_related to make the queryset, which in turn is being used here using obj.
In my case I need design my model in array of fields expected.
{field 1: string, field 2: [""], field 3: [""] }
So far I designed my model like following:
class Collaborator(models.Model):
name = models.CharField(max_length=50, blank=True, null = True, )
class Responsibility(models.Model):
name = models.CharField(max_length=50, blank=True, null = True, )
class Class1(models.Model):
field 1 = models.CharField(max_length=50, blank=True, null = True, )
field 2 = models.ManyToManyField(Responsibility, related_name='res_cards', blank=True,null=True )
field 3 = models.ManyToManyField(Collaborator, related_name='col_cards', blank=True, null=True )
So I am expecting for to get all fields in an array rather than define new model
If you are using SQLite as your database, there is no alternative for ManyToManyField.
However, if you are using (or if you switch to) PostgreSQL as your database, you'll be able to use ArrayField.
You can use it like this:
field_1 = ArrayField(
models.CharField(max_length=50, blank=True)
)
P.S. I assume you aren't really going to name your fields field 1 etc., but if you do, don't use spaces, use underscores instead, so field_1, etc.
There is lots of information on how to use Postgres instead of SQLite, for instance this link would be a good start.
However, in most cases I would still recommend using a ManyToManyField, because it'll enable you to easily query on shared relationships between two objects, and prevent duplicate data as well.
Lets say I have a blog and a class user in a model. Furthermore I have a class comment connected with a foreign key.
class User(models.Model):
UserName = models.CharField(max_length=50, blank=True)
UserCountry = models.CharField(max_length=2, blank=True)
class Comment(models.Model):
commentText = models.TextField(max_length=1000)
commentSub = models.ForeignKey(User, related_name='comLink')
created_at = models.DateTimeField(auto_now_add=True)
Now I want to make an csv export in model admin and a I have a queryset with values_list.
I am wondering whether there exists a possibility to get each User once and e.g. only the last comment?
myList = queryset.values_list('UserName', 'UserCountry', 'comLink__commentText')
comLink is the related name. Now I just want the last comment. A timestamp is existing and I have not figured out how to filter or reverse etc.
You can do it with Subquery, I don`t know your model design, so it would be approximately like that:
from django.db.models import OuterRef, Subquery
com = Comment.objects.filter(commentSub=OuterRef('pk')).order_by('-created_at')
myList = queryset.annotate(LastComment=Subquery(com.values('commentText')[:1]))
myList = myList.values_list('UserName', 'UserCountry', 'LastComment')
https://docs.djangoproject.com/en/2.0/ref/models/expressions/#subquery-expressions
I'm using both django-taggit and django-filter in my web application, which stores legal decisions. My main view (below) inherits from the stock django-filter FilterView and allows people to filter the decisions by both statutes and parts of statutes.
class DecisionListView(FilterView):
context_object_name = "decision_list"
filterset_class = DecisionFilter
queryset = Decision.objects.select_related().all()
def get_context_data(self, **kwargs):
# Call the base implementation to get a context
context = super(DecisionListView, self).get_context_data(**kwargs)
# Add in querysets for all the statutes
context['statutes'] = Statute.objects.select_related().all()
context['tags'] = Decision.tags.most_common().distinct()
return context
I also tag decisions by topic when they're added and I'd like people to be able to filter on that too. I currently have the following in models.py:
class Decision(models.Model):
citation = models.CharField(max_length = 100)
decision_making_body = models.ForeignKey(DecisionMakingBody)
statute = models.ForeignKey(Statute)
paragraph = models.ForeignKey(Paragraph)
...
tags = TaggableManager()
class DecisionFilter(django_filters.FilterSet):
class Meta:
model = Decision
fields = ['statute', 'paragraph']
I tried adding 'tags' to the fields list in DecisionFilter but that had no effect, presumably because a TaggableManager is a Manager rather than a field in the database. I haven't found anything in the docs for either app that covers this. Is it possible to filter on taggit tags?
You should be able to use 'tags__name' as the search/filter field. Check out the Filtering section on http://django-taggit.readthedocs.org/en/latest/api.html#filtering
I have to integrate a legacy design with my Django project and I am looking for some advice on using an intermediary. The existing design works but now I need to filter the Project by a third table.
In english - I have a Organization (Django) and which points to many Projects (Legacy). But all of the Project don't refer to that Organization. I have a third table ProjectMap which was build via a Trigger to address that. It corresponds the Organization.name to a project.
How do I glue this together in order allow me to do this.
projects = Organization.objects.get(pk=1).projects.all()
And it won't get ALL of the projects just the ones which match in the third table. Here is what I have so far..
By the way if anyone has a better strategy I'm all ears
class Organization(models.Model):
name = models.CharField(max_length=32)
projects = models.ManyToManyField(Project)
class Project(models.Model):
"""This is the project info page..
Note: 'id' does exist and is the pk.
"""
result_number = models.IntegerField(null=True, db_column='LBLDGRUNNO', blank=True)
building_number = models.IntegerField(db_column='LBLDGNO')
name = models.CharField(max_length=150, db_column='SPIBLGNAME', blank=True)
class Meta:
db_table = u'PROJINFO'
managed = False
class ProjectMap(models.Model):
projinfo_table_id = models.IntegerField(null=True) # 'id' of Project
name = models.CharField(max_length=128, null=True) # 'name' in Organization
Thanks so much!
Not sure if this is what your asking, but you can use the through call on the ManyToManyField to define an intermediate table:
class Organization(models.Model):
name = models.CharField(max_length=32)
projects = models.ManyToManyField(Project, through="ProjectOrganisation")
class Project(models.Model):
#Stuff Here
class ProjectOrganisation(models.Model):
project = models.ForeignKey(Project)
organization = models.ForeignKey(Organization)
#Other Fields Here
Django does this automatically with manytomany fields anyway, just if you want to add extra fields, this is the way to do it.