Accessing instance attributes that start with a certain string - django

In my view, I'm trying to blank/delete a number of fields that start with real_.
I can do something like:
plan = get_object_or_404(Plan, pk=self.kwargs['plan_id'])
plan.real_time = None
plan.real_date = None
plan.real_comments = None
plan.real_whatever = None
....
plan.save()
However I guess there must be a way to do this programmatically. All I'd need to do is access the names of the the fields, compare whether it indeed starts with real_ and then update that field.
I'm using get_fields() (as per the documentation). I'm not sure though how to do the last part though.
Following is the code of my view:
plan = get_object_or_404(Plan, pk=self.kwargs['plan_id'])
plan_fields = plan._meta.get_fields()
for field in plan_fields:
if field.name[:5] == "real_":
plan.<not sure what to do here> = None
plan.save()
I guess I must be overlooking something small. Any pointer?
Using Django 1.9.

if field.name[:5] == "real_":
setattr(plan, field.name, None)
Python doc.

I would recommend something nice and neat like this:
plan = get_object_or_404(Plan, pk=self.kwargs['plan_id'])
real_fields = [field for field in plan._meta.get_fields() if field.name.startswith('real_')]
for field in real_fields:
setattr(plan, field, None)
plan.save()
This is partially opinion based, but I feel that the use of the list comprehension and .startswith() are slightly more Pythonic.

Related

Django list matching with if == statement

I'm hoping this is super simple. I just started playing around with Django for fun and have been playing around with making a search engine.
I'm setting a temporary list in a views.py file so I set it like this:
tempsearch_list = Name.objects.filter(
Q(name__iexact='Name Not Found')
)
Later in my code I want to see if it's the same as I originally set so I'm trying to do this:
if tempsearch_list == 'Name Not Found':
This never matches. Even if I put it as the next statement after I set it. What am I doing wrong here. This has to be super simple. I've been trying different things for more time than I'd like to admit.
you filtered the objects as tempsearch_list = Name.objects.filter(Q(name__iexact='Name Not Found')) and later on you when you want to compare if tempsearch_list equals to Name Not Found, you don't need any if statment just use if tempsearch_list.exists().
tempsearch_list.exists() will return True if there was an object named Name Not Found else it returns False
how do I check the query set is still equal to the original way it was set
The queryset(tempsearch_list) won't changed unless you reassign it, and that would be always update in case of adding, editing or removing a Name. but again if you wondering, you can compare it with fresh queryset as if tempsearch_list == Name.objects.filter(Q(name__iexact='Name Not Found'))
Ok, so I appreciate everyone who responded. I think this helped a lot. Here's what I did for now. There's probably a better way to do this but it works
tempsearch_queryset1 = Name.objects.filter(
Q(name__iexact='Name Not Found')
)
tempsearch_queryset2 = Name.objects.filter(
Q(name__iexact='Name Not Found')
)
if set(tempsearch_queryset1) == set(tempsearch_queryset2):
Note. Without the set commands it does not work. This doesn't work:
tempsearch_queryset1 = Name.objects.filter(
Q(name__iexact='Name Not Found')
)
tempsearch_queryset2 = Name.objects.filter(
Q(name__iexact='Name Not Found')
)
if tempsearch_queryset1 == tempsearch_queryset2:
Your problem is compare Queryset and string (Queryset is list of model so you can access model by index and access model field by . like model.name
you can do that like
for tempsearch in tempsearch_list :
if tempsearch.name == "Name Not Found":
You should compare Charfield like name field with string

end point to accept multiple values, including query strings

I have a view which should accept an end point with a query parameter, as well as without a parameter.
http://localhost:8001/v1/subjects?owner_ids=62,144
and
http://localhost:8001/v1/subjects
Here's my view file...
class SubjectPagination(JsonApiPageNumberPagination):
"""
Required for frontend to retrieve full list.
"""
max_page_size = 5000
class SubjectViewSet(Subject.get_viewset()):
pagination_class = SubjectPagination
def get_queryset(self):
import pdb; pdb.set_trace()
queryset = Subject.objects.all()
if self.request.GET['owner_ids']:
owner_id_list = self.request.GET['owner_ids'].split(',')
owner_id_list_integer = []
for i in owner_id_list:
owner_id_list_integer.append(int(i))
return queryset.filter(organization__in=owner_id_list_integer)
else:
return queryset
SubjectUserRoleViewSet = Subject.get_by_user_role_viewset(
SubjectViewSet, GroupRoleMap, Role)
I am trying to figure out how to handle both the end points? Please advice what needs to be done at the view to handle a URI with or without query strings?
Here's the urls.py
router.register(r'subjects', views.SubjectViewSet)
First of all, is a good practice to send the parameters in url-form-encode, avoiding things like that, in this case for send a list you could send id as:
?owner_ids[]=62&owner_ids[]=144
the querydict its going to be like this :
<QueryDict: {'owner_ids[]': ['62', '144']}>
and you could process it easily, like this
self.request.GET.getlist('owner_ids[]', [])
remember to use the get and get list functions of the request method GET and POST, to avoid dict errors.
Second, split returns a list the for statement in owner list id is totally unnecessary, and the queryset statement __in accept array of strings, if you actually want to convert all the items to integers use list comprehensions. For example, to convert all the items in a list to integer, just have to use:
owner_ids = [int(i) for i in owner_ids ]
this is way more fast in python and way more pythonic, and also cool too see.
and last, all urls should finish in /, even django has a settings for that called append_slash
this is what i can tell about the ambiguous question you are asking, in the next times please write questions more precisely that help people help you.

Django - Search matches with all objects - even if they don't actually match

This is the model that has to be searched:
class BlockQuote(models.Model):
debate = models.ForeignKey(Debate, related_name='quotes')
speaker = models.ForeignKey(Speaker, related_name='quotes')
text = models.TextField()
I have around a thousand instances on the database on my laptop (with around 50000 on the production server)
I am creating a 'manage.py' function that will search through the database and returns all 'BlockQuote' objects whose textfield contains the keyword.
I am doing this with the Django's (1.11) Postgres search options in order to use the 'rank' attribute, which sounds like something that would come in handy. I used the official Django fulltext-search documentation for the code below
Yet when I run this code, it matches with all objects, regardless if BlockQuote.text actually contains the queryfield.
def handle(self, *args, **options):
vector = SearchVector('text')
query = options['query'][0]
Search_Instance = Search_Instance.objects.create(query=query)
set = BlockQuote.objects.annotate(rank=SearchRank(vector, query)).order_by('-rank')
for result in set:
match = QueryMatch.objects.create(quote=result, query=Search_Instance)
match.save()
Does anyone have an idea of what I am doing wrong?
I don't see you actually filtering ever.
BlockQuote.objects.annotate(...).filter(rank__gte=0.5)

ElasticSearch - bulk indexing for a completion suggester in python

I am trying to add a completion suggester to enable search-as-you-type for a search field in my Django app (using Elastic Search 5.2.x and elasticseach-dsl). After trying to figure this out for a long time, I am not able to figure yet how to bulk index the suggester. Here's my code:
class SchoolIndex(DocType):
name = Text()
school_type = Keyword()
name_suggest = Completion()
Bulk indexing as follows:
def bulk_indexing():
SchoolIndex.init(index="school_index")
es = Elasticsearch()
bulk(client=es, actions=(a.indexing() for a in models.School.objects.all().iterator()))
And have defined an indexing method in models.py:
def indexing(self):
obj = SchoolIndex(
meta = {'id': self.pk},
name = self.name,
school_type = self.school_type,
name_suggest = {'input': self.name } <--- # what goes in here?
)
obj.save(index="school_index")
return obj.to_dict(include_meta=True)
As per the ES docs, suggestions are indexed like any other field. So I could just put a few terms in the name_suggest = statement above in my code which will match the corresponding field, when searched. But my question is how to do that with a ton of records? I was guessing there would be a standard way for ES to automatically come up with a few terms that could be used as suggestions. For example: using each word in the phrase as a term. I could come up something like that on my own (by breaking each phrase into words) but it seems counter-intuitive to do that on my own since I'd guess there would already be a default way that the user could further tweak if needed. But couldn't find anything like that on SO/blogs/ES docs/elasticsearch-dsl docs after searching for quite sometime. (This post by Adam Wattis was very helpful in getting me started though). Will appreciate any pointers.
I think I figured it out (..phew)
In the indexing function, I need to use the following to enable to the prefix completion suggester:
name_suggest = self.name
instead of:
name_suggest = {'input': something.here }
which seems to be used for more custom cases.
Thanks to this video that helped!

Django: How to use django.forms.ModelChoiceField with a Raw SQL query?

I'm trying to render a form with a combo that shows related entities. Therefore I'm using a ModelChoiceField.
This approach works well, until I needed to limit which entities to show. If I use a simple query expression it also works well, but things break if I use a raw SQL query.
So my code that works, sets the queryset to a filter expression.
class ReservationForm(forms.Form):
location_time_slot = ModelChoiceField(queryset=LocationTimeSlot.objects.all(), empty_label="Select your prefered time")
def __init__(self,*args,**kwargs):
city_id = kwargs.pop("city_id") # client is the parameter passed from views.py
super(ReservationForm, self).__init__(*args,**kwargs)
# TODO: move this to a manager
self.fields['location_time_slot'].queryset = LocationTimeSlot.objects.filter(city__id = city_id )
BUT, if I change that to a raw query I start having problems. Code that does not work:
class ReservationForm(forms.Form):
location_time_slot = ModelChoiceField(queryset=LocationTimeSlot.objects.all(), empty_label="Select your prefered time")
def __init__(self,*args,**kwargs):
city_id = kwargs.pop("city_id") # client is the parameter passed from views.py
super(ReservationForm, self).__init__(*args,**kwargs)
# TODO: move this to a manager
query = """SELECT ts.id, ts.datetime_to, ts.datetime_from, ts.available_reserves, l.name, l.'order'
FROM reservations_locationtimeslot AS ts
INNER JOIN reservations_location AS l ON l.id = ts.location_id
WHERE l.city_id = %s
AND ts.available_reserves > 0
AND ts.datetime_from > datetime() """
time_slots = LocationTimeSlot.objects.raw(query, [city_id])
self.fields['location_time_slot'].queryset = time_slots
The first error I get when trying to render the widget is: 'RawQuerySet' object has no attribute 'all'
I could solve that one thanks to one of the commets in enter link description here, by doing:
time_slots.all = time_slots.__iter__ # Dummy fix to allow default form rendering with raw SQL
But now I'm getting something similar when posting the form:
'RawQuerySet' object has no attribute 'get'
Is there a proper way to prepare a RawQuerySet to be used by ModelChoiceField?
Thanks!
Are you sure you actually need a raw query there? Just looking at that query, I can't see any reason you can't just do it with filter(location__city=city_id, available_reserves__gte=0, datetime_from__gt=datetime.datetime.now()).
Raw query sets are missing a number of methods that are defined on conventional query sets, so just dropping them in place isn't likely to work without writing your own definitions for all those methods.
I temporarily fixed the problem adding the missing methods.
The way I'm currently using the ModelChoiceField I only needed to add the all() and get() methods, but in different scenarios you might need to add some other methods as well. Also this is not a perfect solution because:
1) Defining the get method this way migth produce incorrect results. I think the get() method is used to validate that the selected option is within the options returned by all(). The way I temporarily implemented it only validates that the id exists in the table.
2) I guess the get method is less performant specified this way.
If anyone can think of a better solution, please let me know.
So my temporary solution:
class LocationTimeSlotManager(models.Manager):
def availableSlots(self, city_id):
query = """SELECT ts.id, ts.datetime_to, ts.datetime_from, ts.available_reserves, l.name, l.'order'
FROM reservations_locationtimeslot AS ts
.....
.....
MORE SQL """
time_slots = LocationTimeSlot.objects.raw(query, [city_id])
# Dummy fix to allow default form rendering with raw SQL
time_slots.all = time_slots.__iter__
time_slots.get = LocationTimeSlot.objects.get
return time_slots