Django Complex Query: combined output - django

models.py
class DeviceType(models.Model):
device_type = models.CharField(max_length=200,unique=True)
def __str__(self):
return self.device_type
class Device(models.Model):
device_type = models.ForeignKey(DeviceType,to_field='device_type')
serial_number = models.CharField(max_length=200,unique=True)
in_use_by = models.ForeignKey(User,to_field='username')
brand = models.CharField(max_length=200,default="-", null=False)
model = models.CharField(max_length=200,default="-", null=False)
type_number = models.CharField(max_length=200,blank=True,null=True)
mac_address = models.CharField(max_length=200,blank=True,null=True)
Above is my models.py file. I want to write a query such that I get the output in the following format:
device_type-serial_number-model-brand

Processing in Python
We can generate a list containing such strings and let Python construct these strings as follows:
list(map(
'-'.join,
Devices.objects.values_list(
'device_type__device_type',
'serial_number',
'model',
'brand'
)
)
This will return a list of strings.
Processing at the database
We can also perform the concatenation at the database, and then we have a queryset of strings:
from django.db.models import F, Value
from django.db.models.functions import Concat
Devices.objects.annotate(
text=Concat(
F('device_type__device_type'),
Value('-'),
F('serial_number'),
Value('-'),
F('model'),
Value('-'),
F('brand')
)
).values_list('text', flat=True)

Related

Search in several models

I have two model classes as follows that are related to each other with User class.
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
first_name = models.CharField(max_length=30, null=False, blank=False)
last_name = models.CharField(max_length=30, null=False, blank=False)
father_name = models.CharField(max_length=30, null=False, blank=False)
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
code = models.CharField(max_length=15, null=True, blank=True)
And now a request comes as follows:
http://localhost:8000/api/v1/search/users/?first_name=john&&last_name=dev&&code=25
How can I search if one of the input parameters is in one of the tables (person or company)?
The effort I have made but no result found:
class SearchUserAPI(APIView):
def get(self, request, format=None):
try:
from django.db.models import Q
q = request.query_params
search_models = [Person, Company]
search_results = []
for model in search_models:
fields = [x for x in model._meta.fields if isinstance(x, django.db.models.CharField)]
search_queries = [Q({x.name + "__icontains": q.get(x.name)}) for x in fields]
print(search_queries)
q_object = Q()
for query in search_queries:
q_object = q_object | query
results = model.objects.filter(q_object)
search_results.append(results)
data = [search_results]
return Response(data, status=status.HTTP_200_OK)
except Exception as e:
return Response({"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
thats will better and will work i hope )
class SearchUserAPI(APIView):
def get(self, request, format=None):
try:
q = request.query_params
first_name = q.get('first_name')
last_name = q.get('last_name')
code = q.get('code')
data = Person.objects.filter(first_name=first_name, last_name=last_name, company_code=code)
data = [search_results]
return Response(data, status=status.HTTP_200_OK)
except Exception as e:
return Response({"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
also you can easy add another fields to filter
you can use filterset in drf
I assume your user is somehow linked with person or company in Foreign key or One to one
from django_filters import rest_framework as filters, BaseInFilter
class SearchFilter(filters.FilterSet):
first_name = filters.CharFilter(
field_name="person__first_name",
label='person name'
)
last_name = filters.CharFilter(
field_name="person__first_name",
label='person name'
)
code = = filters.NumberFilter(
field_name="company__code",
label='code'
)
class Meta:
model = User
fields = ['first_name','last_name','code']
You can simply call your searchfilter set in views now It will do your work

How to create a dataframe from a model (Django)

I am developing an app in Django.
I have this model
class my_model(models.Model):
Field_A = models.CharField(max_length=256, blank=True, null=True)
Field_B = models.CharField(max_length=25, blank=True, null=True)
Field_C = models.TextField(blank=True, null=True)
I want to create a dataframe with columns names equal to model fields names,
containing in every row the model objects, and in every column the object fields values.
How can I do it? Is there a single command to do it? Or do I have to iterate?
EDIT: Here is the raw and unelegant solution that I found until now:
import pandas as pd
entries = my_model.objects.all()
# this generates an array containing the names of the model fields
columns_names = [field.name for field in my_model._meta.get_fields()]
L_GI = len(entries)
# generate empty dataframe
GI = pd.DataFrame(columns = columns_names)
for element in entries:
new_entry = {"Field_A":element.Field_A, "Field_B":element.Field_B, "Field_C":element.Field_C}
GI = GI.append(new_entry, ignore_index=True)
I bet there is a faster way that avoids iteration. Any suggestions?
Nice question
I think you are forced to iterate them.
I have implemented it in 3 different ways so that you can choose your favorite one
import time
import pandas as pd
from django.core import serializers
class PandasModelMixin(models.Model):
class Meta:
abstract = True
#classmethod
def as_dataframe(cls, queryset=None, field_list=None):
t1 = time.time()
if queryset is None:
queryset = cls.objects.all()
if field_list is None:
field_list = [_field.name for _field in cls._meta._get_fields(reverse=False)]
data = []
[data.append([obj.serializable_value(column) for column in field_list]) for obj in queryset]
columns = field_list
df = pd.DataFrame(data, columns=columns)
print("Execution time without serialization: %s" % time.time()-t1)
return df
#classmethod
def as_dataframe_using_django_serializer(cls, queryset=None):
t1 = time.time()
if queryset is None:
queryset = cls.objects.all()
if queryset.exists():
serialized_models = serializers.serialize(format='python', queryset=queryset)
serialized_objects = [s['fields'] for s in serialized_models]
data = [x.values() for x in serialized_objects]
columns = serialized_objects[0].keys()
df = pd.DataFrame(data, columns=columns)
df = pd.DataFrame()
print("Execution time using Django serializer: %s" % time.time()-t1)
return df
#classmethod
def as_dataframe_using_drf_serializer(cls, queryset=None, drf_serializer=None, field_list=None):
from rest_framework import serializers
t1 = time.time()
if queryset is None:
queryset = cls.objects.all()
if drf_serializer is None:
class CustomModelSerializer(serializers.ModelSerializer):
class Meta:
model = cls
fields = field_list or '__all__'
drf_serializer = CustomModelSerializer
serialized_objects = drf_serializer(queryset, many=True).data
data = [x.values() for x in serialized_objects]
columns = drf_serializer().get_fields().keys()
df = pd.DataFrame(data, columns=columns)
print("Execution time using DjangoRestFramework serializer: %s" % time.time()-t1)
return df
So inherit your Model in this way:
class MyModel(PandasModelMixin):
field_a = models.CharField(max_length=256, blank=True, null=True)
field_b = models.CharField(max_length=25, blank=True, null=True)
field_c = models.TextField(blank=True, null=True)
and try the code in this way:
>> MyModel.as_dataframe()
>> MyModel.as_dataframe_using_django_serializer()
>> MyModel.as_dataframe_using_drf_serializer()
I have tried my code using a Model with 450 instances and 15 columns and I had these results:
Execution time without serialization: 0.07040905952453613
Execution time using Django serializer: 0.07644820213317871
Execution time using DjangoRestFramework serializer: 0.12314629554748535
N.B.
I'm using Django 2.2 and Python 3.6.5

django-elasticsearch-dsl unable to filter with mulitiple model fields

models.py
class PostJob(models.Model):
job_title = models.CharField(max_length=256)
job_description = models.TextField()
key_skills = models.TextField()
def __str__(self):
return self.job_title
documents.py
from django_elasticsearch_dsl import DocType, Index
from .models import PostJob
jobs = Index('jobs')
#jobs.doc_type
class JobsDocument(DocType):
class Meta:
model = PostJob
fields = [
'job_title',
'job_description',
'key_skills',
]
views.py
from .documents import JobsDocument
def search_jobs(request):
q = request.GET.get('q')
if q is None:
return JsonResponse({"code":500,"msg":"query sting not found"})
if q:
Q = JobsDocument.search().query
jobs = Q("match", key_skills=q) or Q("match", job_title=q)
lst=[]
dict ={}
for i in jobs:
dict["job_title"] = i.job_title
dict["description"] = i.job_description
dict["key_skills"] = i.key_skills
lst.append(dict.copy())
return JsonResponse(lst,safe=False)
in django using 'django-elasticsearch-dsl' i am trying to search with multiple model fields.
here i wants to filter with multiple fields with key_skills and job_title
but it is coming with only key_skills but doesn't matches with job_description
for job_title if my job_title job python developer it is not coming if i am searching only developer. it is coming when i am searching python developer completely with white space
Please have a look into it..

Filtering Django models by user & object

I'm learning Django with a dummy example but having difficulty in understanding how to correctly filter my Django models by an authorised user in my views.
In my view I want to list the transactions associated with a users portfolio. The code below runs but when trying to access the result of 't' I get the error:
'ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.'
Any help would be much appreciated, thanks.
if request.user.is_authenticated:
# Get model data
pf = Portfolio.objects.filter(user=request.user)
t = Transaction.objects.filter(pf=pf)
My model is as below:
from django.db import models
from django.contrib.auth.models import User
class Portfolio(models.Model):
# Portfolio has one user associated with it
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, default='-')
def __str__(self):
return self.name
class Transaction(models.Model):
# Transaction has one equity associated with it
equity = models.ForeignKey('Equity', on_delete=models.CASCADE, null=True)
# Transaction has one portfolio associated with it
pf = models.ForeignKey('Portfolio', on_delete=models.CASCADE)
BUY = 'BUY'
SELL = 'SELL'
BUY_OR_SELL = (
(BUY, 'BUY'),
(SELL, 'SELL'),
)
action = models.CharField(choices=BUY_OR_SELL, default=BUY, max_length=5)
num = models.FloatField(default=1)
price = models.FloatField(default=0)
date = models.DateField('date')
fee = models.FloatField(default=0)
def __str__(self):
return f'{self.equity}, {self.num}x{self.price}, {self.date:%d %b %Y}'
class Equity(models.Model):
class Meta:
verbose_name_plural = "Equities"
CUR_EUR = 'EUR'
CUR_GBP = 'GBP'
CUR_USD = 'USD'
CURRENCY_CHOICES = (
(CUR_EUR, 'EUR'),
(CUR_GBP, 'GBP'),
(CUR_USD, 'USD'),
)
symbol = models.CharField(max_length=20, default='-')
exchange = models.CharField(max_length=100, default='-')
currency = models.CharField(max_length=15, choices=CURRENCY_CHOICES, default=CUR_USD)
def __str__(self):
return self.symbol
Many thanks!
pf is here a collection of Portfolio objects, so you can query it with the __in lookup [Django-doc]:
Transaction.objects.filter(pf__in=pf)
Or if you are not interested in the Porfolio objects itself, you can make a query like:
Transaction.objects.filter(pf__user=request.user)
The query below will result in a query like:
SELECT transaction.*
FROM transaction
JOIN portfolio ON transaction.pf_id = portfolio.id
WHERE porfolio.user_id = 123
(with 123 the id of the request.user)

DJANGO: AUTO ADD THE RESPECTIVE FOREIGN KEY VALUE IN CREATE VIEW

I am creating a CreateView with the following models:
from django.db import models
from uuid import uuid4
from django.core.validators import MinValueValidator,MaxValueValidator
from questions.models import Question
from django.urls import reverse
class ExamPaper(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
name = models.CharField(max_length=300)
for_class = models.PositiveSmallIntegerField(
validators=(
MinValueValidator(1),
MaxValueValidator(12),
))
date = models.DateField(null=True, blank=True)
PARTS = (
("A", "A"),
("B", "B"),
("C", "C"),
("D", "D"),
("E", "E"),
)
part = models.CharField(
max_length=1, choices=PARTS, null=True,blank=True)
def __str__(self):
return f"{self.name} ({self.get_part_display()})"
def get_absolute_url(self):
return reverse("exam_detail", args=[str(self.pk)])
class ExamQuestions(models.Model):
exam = models.ForeignKey(
ExamPaper, on_delete=models.SET_NULL, null=True,
related_name="examquestions")
question = models.ForeignKey(
Question, on_delete=models.CASCADE,)
marks = models.PositiveSmallIntegerField(
validators=(MaxValueValidator(20), ))
def __str__(self):
return self.question.question[:150]
def get_absolute_url(self):
return reverse("exam_detail", args=[str(self.exam.pk)])
I am trying to make a form using CreateView in which I can add questions and marks to a ExamPaper. I have allready created a CreatView using ExamPaper model and added a link in it to add questions.
What i want is that whenever I add questions the exam field gets assigned to that question.
views.py
class ExamCreateView(CreateView):
model = ExamPaper
template_name = "exam_new.html"
fields = "__all__"
class ExamQuestionsView(CreateView):
model = ExamQuestions
fields = "question", "marks",
template_name = "exam_ques_new.html"
I am able to get a form for both but in ExamQuestionView I have to select exam from the whole list.