Djanjo with Rest Framework and Postgres search - python

I need to make search for looking some data from database and this search field should support some logical operations. Like AND, OR, NOT and so on.
So I found that Postgres db has something that I need. Ad Django documentation saying to us we need to use SearchQuery with parameter search_type='websearch'
And we have something like
SearchQuery("'tomato' ('red' OR 'green')", search_type='websearch') # websearch operators
When I'm tried to implement to my project I did something like that
# views.py
class DataSearch(generics.ListAPIView):
serializer_class = DataSerializer
def get_queryset(self):
queryset = Data.objects.all()
query = self.request.query_params.get('query')
if query is not None:
queryset = queryset.filter(
search_vector = SearchQuery(
query,
search_type='websearch'
)
)
return queryset
But when I'm trying to run it I get
Cannot resolve keyword 'search_vector' into field. Choices are: data, foo, foo_id
So what I should do to make it work and to make API endpoint for this type of search?

Related

Specify a Django model field as a query expression

How do I specify that a model field is implemented by a database expression instead of a table column?
I have this model:
class URLProbe(models.Model):
url = models.CharField(max_length=MAX_URL_LENGTH)
timestamp = models.DateTimeField(auto_now_add=True)
status_code = models.SmallIntegerField(null=True, blank=True) # The HTTP status code
error = models.TextField(blank=True) # stores non-HTTP errors, e.g. connection failures
def ok(self):
return self.status_code == 200
ok.boolean = True
ok.admin_order_field = Case(
When(status_code__exact=200, then=True),
default=False,
output_field=BooleanField()
)
def errmsg(self):
if self.ok():
return ''
if self.status_code is not None:
return f'Error: HTTP status {self.status_code}'
return self.error
errmsg.admin_order_field = Case(
When(status_code__exact=200, then=Value('')),
When(status_code__isnull=False,
then=Concat(Value('Error: HTTP status '), 'status_code')),
default='error',
output_field=CharField()
)
The model represents an attempt to retrieve an url, recording whether the HTTP call succeeded or what the error was.
The ok and errmsg fields here are implemented as methods, but I want to be able to use them in the Django admin as regular fields (sort/filter on them) and to sort and/or filter on them in queries. That is possible, but I need to define the query expression multiple times: in an admin_order_field, in a Django admin custom filter, and in queries where I want to use them. So that duplicates a lot of code, both for the multiple expressions and between the query expression and the python method.
What I would like to do
is define fields as query expressions, allowing any database operations to occur without further configuration. Something like:
class URLProbe(models.Model):
...
ok = ExpressionField(Case(
When(status_code__exact=200, then=True),
default=False,
output_field=BooleanField()
))
errmsg = ExpressionField(Case(
When(ok__exact=True, then=Value('')),
When(status_code__isnull=False,
then=Concat(Value('Error: HTTP status '), 'status_code')),
default='error',
output_field=CharField()
))
However I have not been able to find anything like ExpressionField or a way to do something similar. Does this exist? Can this be implemented in Django?
update: I found something that works partially. I can specify a custom manager with a default queryset that includes ok and errmsg as annotations. That allows me to use them in custom queries without duplicating the expression, but unfortunately the admin does not accept them as fields and throws SystemCheckErrors. I feel that it should work if it were not for that system check.
As far as I know, using the base Django ORM you cannot sort by properties since those operations are done at SQL level and that field is not stored in the database.
There is a project to denormalize models, that basically calculates and stores this calculated values behind the curtain for you, that would probably serve your purpose:
https://github.com/django-denorm/django-denorm
So you would use it like:
#denormalized(models.CharField, max_length=100)
def errmsg(self):
if self.ok():
return ''
if self.status_code is not None:
return f'Error: HTTP status {self.status_code}'

How do I expose Wagtail search in a Django Graphene query?

Given the general structure:
class Article(Page):
body = RichTextField(...)
search_fields = Page.search_fields + [index.SearchField('body')]
class ArticleFilter(FilterSet):
search = SearchFilter()
class Meta:
model = Article
fields = ['slug']
class Query(ObjectType):
articles = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleFilter)
I thought to create a "SearchFilter" to expose the wagtail search functionality, since I ultimately want to perform full text search via graphql like so:
query {
articles (search: "some text in a page") {
edges {
nodes {
slug
}
}
}
}
"search" is not a field on the Django model which is why I created a custom field in the Django FilterSet. My thought was to do something like:
class SearchFilter(CharFilter):
def filter(self, qs, value):
search_results = [r.pk for r in qs.search(value)]
return self.get_method(qs)(pk__in=search_results)
however, I'm curious if there's a better pattern that's more efficient. At the bare minimum I'd want to ensure the SearchFilter is added last (so the query searched is filtered first).
Should the "search" be moved outside of the FilterSet and into the Query/Node/custom connection, and if so, how can I add an additional field to "articles" to see it as the final step in resolving articles (i.e. tack it on to the end of the filter queryset)? If this does belong in a separate Connection, is it possible to combine that connection with the django filter connection?
I would think this pattern of accessing Wagtail search via graphene already exists, however I've had no luck on finding this in the documentation.

DJANGO:How to perform AND operation for my query?

There are two models .I want to make query to extract only the app exact app related Adspaces .
models.py
class Appname(models.Model):
user=models.ForeignKey(User,related_name='appname', null=True, default=None,on_delete=models.CASCADE)
name=models.CharField(max_length=150,blank=False,null=False,help_text='Add your new App')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("dashapp:space",kwargs={'pk':self.pk})
class Adspace(models.Model):
user=models.ForeignKey(User,related_name='adspace', null=True, default=None,on_delete=models.CASCADE)
ad_space=models.CharField(max_length=150,blank=False,null=False)
app=models.ForeignKey('Appname', related_name='appnames',default=None, on_delete=models.CASCADE)
PID_TYPE = (
('FN','FORMAT_NATIVE'),
('FNB','FORMAT_NATIVE_BANNER'),
('FI','FORMAT_INTERSTITIAL'),
('FB','FORMAT_BANNER'),
('FMR','FORMAT_MEDIUM,RECT'),
('FRV','FORMAT_REWARDED_VIDEO'),
)
format_type=models.CharField(max_length=3,choices=PID_TYPE,default='FN',blank=False, null=False)
def __str__(self):
return self.ad_space
def get_absolute_url(self):
return reverse("dashapp:create",kwargs={'pk':self.pk})
Views.py
SHowing the one where i need to the query
class spacelist(LoginRequiredMixin,ListView):
model=Adspace
template_name='adspace_list.html'
def get_queryset(self):
query_set=super().get_queryset()
return query_set.filter(user=self.request.user)
Here I need to perform One more query so that EACH APP show their own adspaces when clicked right now every app show every show adspaces.
I have the idea what to do as if i compare app_id then it'll show the exact app related adspaces, but i dont know how to write query for the same as i already have one query present.???
You could try using a Q objects: https://docs.djangoproject.com/en/2.1/topics/db/queries/#complex-lookups-with-q-objects
From what I understand you are trying to filter both on the app_id and the request user at the same time, so you could try look something like this:
from django.db.models import Q
...
def get_queryset(self):
query_set=super().get_queryset()
return query_set.filter(Q(user=self.request.user) & Q(app_id=app_id))
...
This lets you do a single filter with both your requirements at the same time (i.e. retrieve the Adspace instances for a specific user with a specific Appname).
You chain another filter at the end like this:
class spacelist(LoginRequiredMixin,ListView):
model=Adspace
template_name='adspace_list.html'
def get_queryset(self):
query_set = super().get_queryset()
query_set = query_set.filter(user=self.request.user)
app_id = [...]
return query_set.filter(app_id=app_id)
The problem left is to find out what is the app_id coming from. How do you know what is the current app? Several options here.
Option 1: From the request
It can come from the current user: self.request.user.appname.all() but that will give you multiple apps, if the user can only have one app, you should change your model Appname.user to a OneToOneField.
Otherwise, I suggest changing your related_name='appnames' to reflect the multiplicity in the reverse relationship.
Option 2: From the URL
It can come from the URL, your space list view should extract an app_id parameter from the URL where it's defined:
url(r'^(?P<app_id>[0-9]+)/spaces/$', spacelist.as_view(), name='space_list'),
And then in the spacelist view, you would get this parameter like this:
app_id = self.kwargs['app_id']
return query_set.filter(app_id=app_id)
Hope that helps
UPDATE:
Also worth noting that QuerySets are lazy, meaning the result will get evaluated as late as possible by Django. Therefore, when you call:
query_set = query_set.filter(user=self.request.user)
The Django ORM doesn't execute any DB queries yet, and you can chain more filters after that:
query_set = query_set.filter(user=self.request.user)
query_set = query_set.filter(app_id=app_id)
Which behind the scenes is extending the query that will be executed when required. But at this point, no query is actually run. To see the query that will get executed you can print out the query attribute of the QuerySet:
print(query_set.query)
Which should log something like:
SELECT "app_adspace"."user_id" ...
FROM
"app_adspace"
WHERE
"app_adspace"."user_id" = 1234 AND "app_adspace"."app_id" = 5678

How to define a selection field in flask

I want to define a selection field in python, i.e. field that is limited to a set of values. How can I do that in flask framework. I could not find anything on selection fields in the following sources:
Declaring Models
SQLAlchemy in Flask
I am using sqlalchemy for ORM.
I assume you mean a field in a form that has a limited set of options; to do this you can use WTForms and its extensions which allow you to create forms from models.
Once you have done that, you can then limit the choices for a field based on a model condition.
As you haven't posted your model, here is the example give you give you an idea on how this would work:
def enabled_categories():
return Category.query.filter_by(enabled=True)
class BlogPostEdit(Form):
title = TextField()
blog = QuerySelectField(get_label='title')
category = QuerySelectField(query_factory=enabled_categories,
allow_blank=True)
def edit_blog_post(request, id):
post = Post.query.get(id)
form = ArticleEdit(obj=post)
# Since we didn't provide a query_factory for the 'blog' field, we need
# to set a dynamic one in the view.
form.blog.query = Blog.query.filter(Blog.author == request.user) \
.order_by(Blog.name)

Returning extended fields in JSON

I have two tabels(Ingredient_Step and Ingredient) in on relation as you can see below:
Models.Py
class Ingredient_Step(models.Model):
ingredient = models.ForeignKey(Ingredient)
Step = models.ForeignKey(Step)
def __unicode__(self):
return u'{}'.format(self.Step)
class Ingredient(models.Model):
IngredientName = models.CharField(max_length=200,unique=True)
Picture = models.ImageField(upload_to='Ingredient')
def __unicode__(self):
return u'{}'.format(self.IngredientName)
In a function, i need serialize a JSON object from a query that returns from "Ingredient_step", but I need send the field "IngredientName", who comes from "Ingredient" table.
I try using "ingredient__IngredientName" but it fails.
Views.Py:
def IngredientByStep(request):
if request.is_ajax() and request.GET and 'id_Step' in request.GET:
if request.GET["id_Step"] != '':
IngStp = Ingredient_Step.objects.filter(Step =request.GET["id_Step"])
return JSONResponse(serializers.serialize('json', IngStp, fields=('pk','ingredient__IngredientName')))
How i can call extends field from a relation?
Thanks
This "feature" of Django (and many ORM's like SQLAlchemy) are called Lazy Loading, meaning data is only loaded from related models if you specifically ask for them. In this case, build your IngStp as a list of results, and make sure to access the property for each result before serializing.
Here's an example of how to do that: Django: Include related models in JSON string?

Categories