I want to implement a search in Elastic Search with Django Rest Framework. I have a form for searching as follows.
I used a serializer to implement this form.
search.py:
class AdvancedSearch(mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = AdvancedSearchSerializer
def query_builder(self, *args, **kwargs):
## building related query
return query
#get_db()
def get_queryset(self, db=None, *args, **kwargs):
serializer = self.get_serializer(data=self.request.data)
serializer.is_valid(raise_exception=True)
query = self.query_builder(search_input=serializer.validated_data)
response = db.search(query) # query the elastic with elasticsearch-dsl and return the results
if not response:
raise NoteFound()
return response
def list(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
self.serializer_class = AdvancedSearchSerializer
return super(AdvancedSearch, self).list(request, *args, **kwargs)
serializer.py:
class AdvancedSearchSerializer(serializers.Serializer):
metadata_choices = [('', ''), ...]
name = serializers.CharField(required=False, label='Name')
type = serializers.CharField(required=False, label='Type')
metadata = serializers.CharField(required=False, label='Metadata')
metadata_fields = serializers.MultipleChoiceField(allow_blank=True, choices=metadata_choices)
submit_date = serializers.DateTimeField(required=False)
def to_representation(self, instance):
output = {}
output['es_id'] = instance.meta.id
for attribute_name in instance:
attribute = getattr(instance, attribute_name)
if isinstance(attribute, (str, int, bool, float, type(None))):
# Primitive types can be passed through unmodified.
output[attribute_name] = attribute
elif isinstance(attribute, list):
# Recursively deal with items in lists.
output[attribute_name] = [
self.to_representation(item) for item in attribute
]
elif isinstance(attribute, (dict, AttrDict)):
temp = attribute.to_dict()
for key, value in temp.items():
print(key,value)
# Recursively deal with items in dictionaries.
output[attribute_name] = {
str(key): value
for key, value in temp.items()
}
else:
# Force anything else to its string representation.
output[attribute_name] = attribute
output['highlight'] = instance.meta.highlight.to_dict()
return [output]
With this code, I get the expected result, but I was wondering if this is a right approach.
And also in to_representation I have access to each result, but how can I add a total value like the number of results.
Thanks in advance.
Related
suppose i am adding matchround_5, round 1,2,3,4 must have inside the db ,check each round are present in the database , How can i solve this issue?
Modelserializer
class MatchScoreSerializer(serializers.ModelSerializer):
def create(self, validated_data):
gameevent = validated_data.get('gameevent')
match_round =validated_data.get('match_round')
match = Matchscore.objects.filter(gameevent=gameevent,match_round=match_round).first()
if match :
validated_data = Matchscore.objects.filter(gameevent=gameevent,match_round=match_round).update(**validated_data)
match.refresh_from_db()
return validated_data
else:
return Matchscore.objects.create(**validated_data)
def validate(self, validated_data):
gameevent = validated_data.get('gameevent')
match_round =validated_data.get('match_round')
if not match_round :
raise serializers.ValidationError("Enter value more than 0")
if match_round == 1:
return validated_data
round = range(1,match_round)
match = Matchscore.objects.filter(gameevent=gameevent,match_round__in=round).values_list('match_round',flat=True)
match = set(match)
if match :
return validated_data
if len(match)== match_round-1:
return validated_data
else:
raise serializers.ValidationError("Enter a valid match_round")
Here my model
class Matchscore(TimeStampedModel):
gameevent = models.ForeignKey(GameEvent, null=True, related_name='game_event',on_delete=models.DO_NOTHING)
match_round = models.IntegerField(null=True,blank=True)
team_a = models.ForeignKey(Team,null=True,related_name='team_one',on_delete=models.DO_NOTHING)
team_a_score = models.PositiveIntegerField(null=True,blank=True)
team_b = models.ForeignKey(Team,null=True,related_name='team_two',on_delete=models.DO_NOTHING)
team_b_score = models.PositiveIntegerField(null=True,blank=True)
team_won = models.ForeignKey(Team,null=True,related_name='team', on_delete=models.DO_NOTHING)
Checking if match to return the validated data is not sufficient. You should check the length. You thus return the validated data if len(match) == match_round - 1.
You can however let the database return the number of distinct values, and thus avoid counting yourself:
matches = Matchscore.objects.filter(
gameevent=gameevent,
match_round__range=(1, match_round)
).values('match_round').distinct().count()
if matches == match_round-1:
return validated_data
else:
raise serializers.ValidationError('Enter a valid match_round')
i want to pass completely and partially paid users in my serializer,but its not shows
,
payment_status = serializers.SerializerMethodField()
def get_payment_status(self, obj):
if int(obj.total_due) > 0:
paid_status ={'payment_status':'Partially paid'}
else:
paid_status={'payment_status':'Completely paid'}
return paid_status
Try to return String, not dict
payment_status = serializers.SerializerMethodField()
def get_payment_status(self, obj):
if int(obj.total_due) > 0:
return 'Partially paid'
return 'Completely paid'
I have a ModelSerializer class as follows which I want to accept a list of items or a single item (dictionary) as data. The documentation states that passing "many" as True will support my requirement.
class PointSerializer(serializers.ModelSerializer):
class Meta:
model = Point
def __init__(self, *args, **kwargs):
if "data" in kwargs:
if isinstance(kwargs["data"]):
kwargs["many"] = True
super(PointSerializer, self).__init__(*args, **kwargs)
Now, providing data dictionary as follows works:
p = PointSerializer(data={'x':10, 'y': 12})
p.is_valid() # True
But this, with a list of dictionaries, fails:
p = PointSerializer(data=[{'x':10, 'y':12}, {'x':12, 'y':12}])
p.is_valid() # False
p.errors() # {'non_field_errors': ['Invalid data. Expected a dictionary, but got a list.']}
UPDATE:
Thanks to the chosen answer, I've changed my code to the following and it works fine:
class PointSerializer(serializers.ModelSerializer):
class Meta:
model = Point
>>> ps = PointSerializer(data={'x':10, 'y':12})
>>> ps.is_valid()
... True
>>> ps = PointSerializer(data=[{'x':10, 'y':12}, {'x':12, 'y':12}], many=True)
>>> ps.is_valid()
... True
many=True argument will only work when instantiating the serializer because it'll return a ListSerializer behind the scene.
Your option are either you set the many=True as serializer argument during creation call, either use explicitly the ListSerializer.
I am trying to make searches using haystack and elasticsearch but for some reason it only returns results when I search a word with more than one character.
How do I enable one character searches?
This is my index
class CustomerIndex(CelerySearchIndex, indexes.Indexable):
text = indexes.CharField(document=True)
first_name_auto = indexes.CharField(model_attr='first_name', default='')
last_name_auto = indexes.CharField(model_attr='last_name', default='')
full_name_auto = indexes.EdgeNgramField(model_attr='get_full_name', default='')
address1 = indexes.CharField(model_attr='address1', indexed=False,
default='')
accounts_customer_auto = indexes.MultiValueField()
def get_model(self):
return Customer
def prepare_accounts_customer_auto(self, obj):
return [a.id for a in obj.accounts.all()]
# def index_queryset(self, using=None):
# """Used when the entire index for model is updated."""
# return Customer.objects.all()
def get_model(self):
return Customer
and this is the query
sqs = SearchQuerySet().filter(
full_name_auto__exact=query_text).order_by('-full_name_auto').models(Customer)
This seems to be an haystack default feature
this is my form:
class IPTrackerSearchForm(forms.Form):
keyword = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'size':'50'}))
search_in = forms.ChoiceField(required=False, choices=ANY_CHOICE + MODULE_SEARCH_IN_CHOICES)
product = forms.CharField(max_length=64,widget=forms.TextInput(attrs={'size':'50'}))
family = forms.CharField(max_length=64,widget=forms.TextInput(attrs={'size':'50'}))
temp_result = Merlin.objects.values('build').distinct()
result = [(value['build'], value['build']) for value in temp_result]
build = forms.ChoiceField(choices=ANY_CHOICE + result)
circuit_name = forms.CharField(max_length=256,widget=forms.TextInput(attrs={'size':'50'}))
parameterization = forms.CharField(max_length=1024,widget=forms.TextInput(attrs={'size':'50'}))
metric = forms.CharField(max_length=64,widget=forms.TextInput(attrs={'size':'50'}))
show_in_one_page = forms.BooleanField(required=False, label="Show filtered result in one page", widget=forms.CheckboxInput(attrs={'class':'checkbox'}))
def __init__(self, *args, **kwargs):
super(IPTrackerSearchForm, self).__init__(*args, **kwargs)
temp_result = Merlin.objects.values('build').distinct()
self.result = [(value['build'], value['build']) for value in temp_result]
self.build = forms.ChoiceField(choices=ANY_CHOICE + self.result)
print self.result
With the purpose that, each time I refresh the webpage, when have new record to "build" column in database. It should update to the drop down box "build" here but It never update unless restart the server. I use print and see that __init__ detect new record but can not refect to build in Class.
Many thanks
You actually need to update self.fields['build'] instead of self.build.
def __init__(self, *args, **kwargs):
super(IPTrackerSearchForm, self).__init__(*args, **kwargs)
temp_result = Merlin.objects.values('build').distinct()
result = [(value['build'], value['build']) for value in temp_result]
self.fields['build'] = forms.ChoiceField(choices=ANY_CHOICE + result)
print result
Because you're not updating self.fields, you are seeing the result of the query at compile time, not execution.