Prepare a string representation of a command and then execute id - django

Django==2.2.2
Now I have this code:
Model
class YandexResponse(models.Model):
time = models.DateTimeField(auto_now=True)
campaigns = models.TextField()
ad_groups = models.TextField()
ads = models.TextField()
keywords = models.TextField()
sitelinks = models.TextField()
View
yandex_response_record, created = YandexResponse.objects.get_or_create(id=1)
if what_to_update == "campaigns":
yandex_response_record.campaigns=json.dumps(data)
yandex_response_record.save()
elif what_to_update == "ad_groups":
yandex_response_record.ad_groups=json.dumps(data)
yandex_response_record.save()
...
I'd like to have something like:
tmp = "yandex_response_record.{}=json.dumps(data)".format(what_to_update)
execute(tmp);
yandex_response_record.save()
Could you tell me whether is is somehow possible? If it is not possible, could you suggest me some elegant solution here?

For this you can use the setattr function as specified in the docs.
'setattr' is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute.
So instead of checking for the value of the field passed, you can do:
yandex_response_record, created = YandexResponse.objects.get_or_create(id=1)
setattr(yandex_response_record, what_to_update, json.dumps(data))
yandex_response_record.save()
What it does is updates the field name specified in what_to_update as provided in json.dumps(data).
Maybe it will be helpful to you.

You are looking for the built-in setattr function.
setattr(yandex_response_record, what_to_update, json.dumps(data))
yandex_response_record.save()

Related

How to correctly write a condition in view Django?

The meaning of the program is to select analogues from the list and link them. I bind all values.
I think the problem is in the wrong if. How to fix it
My view:
def editpart(request, id, **kwargs):
if request.method == 'POST':
part.name = request.POST.get("name")
part.description = request.POST.get("description")
analogs = Part.objects.all()
for analog_zap in analogs:
analog = analog_zap.analog
if Part.objects.filter(analog_analog = analog):
part.analog.add(analog_zap.id)
My model:
class Part(models.Model):
name = models.CharField('Название', max_length=100)
analog = models.ManyToManyField('self', blank=True, related_name='AnalogParts')
I'm just going to assume you have gotten a part object before trying to assign the following:
part.name = request.POST.get("name")
part.description = request.POST.get("description")
Whether this was intended to update the "retrieved part object" or not, I'd recommend that you have instead two fresh variables to collect the info from the post request, then update the part object with these if any related info were found in the database.
name = request.POST.get("name")
description = request.POST.get("description")
As for the real issue here... Not sure I'm understanding your question much but I do see a problem with one of your if statements though. You have:
for analog_zap in analogs:
analog = analog_zap.analog
if Part.objects.filter(analog_analog = analog): # problem here...
part.analog.add(analog_zap.id)
As you've posted:
class Part(models.Model):
name = models.CharField('Название', max_length=100)
analog = models.ManyToManyField('self', blank=True, related_name='AnalogParts') # right here...
The Part model has a field called analog, not analog_analog: maybe you meant analog__analog. But maybe you should try checking by: if Part.objects.filter(analog__pk=analog.pk) or if Part.objects.filter(analog__in=[analog]).distinct().
Furthermore, what you might want to do is to check if something exists, so add .exists() to the end of if Part.objects.filter(analog__pk=analog.pk). Example:
if Part.objects.filter(analog__pk=analog.pk).exists():
...
Looking like:
for analog_zap in analogs:
analog = analog_zap.analog
if Part.objects.filter(analog__pk=analog.pk).exists():
part.analog.add(analog_zap.id)
# update the name and description here as well with the values retrieved from the post request
part.name = name
part.description = description
You could try that.
UPDATES
What worked in this context is:
Part.objects.filter(analog__in=[analog_zap]).distinct()

Get verbose name of foreign key from objects _meta dynamically in Django

For simplicity sake, with models like the following:
class Plan(models.Model)
name = models.CharField(max_length=255, verbose_name="Phone Plan Name")
monthly_charge = models.FloatField()
class Company(models.Model):
name = models.CharField(max_length=255, verbose_name="Company Full Name")
phone_plan = models.ForeignKey(Plan)
class Client(models.Model):
name = models.CharField(max_length=255, verbose_name="Client Full Name")
company = models.ForeignKey(Company)
Given a string, I want to know if there is an easy way to retrieve the verbose name of a model attribute even if that string traverses through foreign keys.
I know that I can get the verbose name of a Client attribute by
Client._meta.get_field("name").verbose_name
and this would result in "Client Full Name".
But what if I had the string "company__phone_plan__name", I cannot simply use
Client._meta.get_field("company__phone_plan__name").verbose_name
to arrive at "Phone Plan Name" as it yields an error.
These strings will be dynamic, so I am wondering what is the easiest way to arrive at the proper verbose name of an attribute, even if it traverses models?
This particular case is using Django 1.11
This is not so good answer but if you need what you want you can use this function:
def get_verbose_name(model, string):
fields = string.split('__')
for field_name in fields[:-1]:
field = model._meta.get_field(field_name)
if field.many_to_one:
model = field.foreign_related_fields[0].model
elif field.many_to_many or field.one_to_one or field.one_to_many:
model = field.related_model
else:
raise ValueError('incorrect string')
return model._meta.get_field(fields[-1]).verbose_name
This function gets model name and string and return verbose name
You can use it like so:
get_verbose_name(Client, 'company__phone_plan__name')
If you are working with instances of the models, then you could try this:
c = Client.objects.first()
# c._meta.get_field('company').verbose_name
# c.company._meta.get_field('phone_plan').verbose_name
c.company.phone_plan._meta.get_field('name').verbose_name
If you are working with classes only, then its a but more complex:
field_company = Client._meta.get_field('company')
field_phone_plan = field_company.rel.to._meta.get_field('phone_plan')
field_name = field_phone_plan.rel.to._meta.get_field('name')
field_name.verbose_name
# or one long line
s = Client._meta.get_field('company') \
.rel.to._meta.get_field('phone_plan') \
.rel.to._meta.get_field('name') \
.verbose_name
EDIT:
User #AndreyBerenda points out that the second option does not work for him in Django 2.1; I tested only in 1.11, which was specified in the question.

Query intermediate through fields in django

I have a simple Relation model, where a user can follow a tag just like stackoverflow.
class Relation(models.Model):
user = AutoOneToOneField(User)
follows_tag = models.ManyToManyField(Tag, blank=True, null=True, through='TagRelation')
class TagRelation(models.Model):
user = models.ForeignKey(Relation, on_delete=models.CASCADE)
following_tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
pub_date = models.DateTimeField(default=timezone.now)
class Meta:
unique_together = ['user', 'following_tag']
Now, to get the results of all the tags a user is following:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
tags_following = kakar.relation.follows_tag.all()
This is fine.
But, to access intermediate fields I have to go through a big list of other queries. Suppose I want to display when the user started following a tag, I will have to do something like this:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
kakar_relation = Relation.objects.get(user=kakar)
t1 = kakar.relation.follows_tag.all()[0]
kakar_t1_relation = TagRelation.objects.get(user=kakar_relation, following_tag=t1)
kakar_t1_relation.pub_date
As you can see, just to get the date I have to go through so much query. Is this the only way to get intermediate values, or this can be optimized? Also, I am not sure if this model design is the way to go, so if you have any recomendation or advice I would be very grateful. Thank you.
You need to use Double underscore i.e. ( __ ) for ForeignKey lookup,
Like this :
user_tags = TagRelation.objects.filter(user__user__email="kakar#gmail.com").values("following_tag__name", "pub_date")
If you need the name of the tag, you can use following_tag__name in the query and if you need id you can use following_tag__id.
And for that you need to iterate through the result of above query set, like this:
for items in user_tags:
print items['following_tag__name']
print items['pub_date']
One more thing,The key word values will return a list of dictionaries and you can iterate it through above method and if you are using values_list in the place of values, it will return a list of tuples. Read further from here .

django querset filter foreign key select first record

I have a History model like below
class History(models.Model):
class Meta:
app_label = 'subscription'
ordering = ['-start_datetime']
subscription = models.ForeignKey(Subscription, related_name='history')
FREE = 'free'
Premium = 'premium'
SUBSCRIPTION_TYPE_CHOICES = ((FREE, 'Free'), (Premium, 'Premium'),)
name = models.CharField(max_length=32, choices=SUBSCRIPTION_TYPE_CHOICES, default=FREE)
start_datetime = models.DateTimeField(db_index=True)
end_datetime = models.DateTimeField(db_index=True, blank=True, null=True)
cancelled_datetime = models.DateTimeField(blank=True, null=True)
Now i have a queryset filtering like below
users = get_user_model().objects.all()
queryset = users.exclude(subscription__history__end_datetime__lt=timezone.now())
The issue is that in the exclude above it is checking end_datetime for all the rows for a particular history object. But i only want to compare it with first row of history object.
Below is how a particular history object looks like. So i want to write a queryset filter which can do datetime comparison on first row only.
You could use a Model Manager method for this. The documentation isn't all that descriptive, but you could do something along the lines of:
class SubscriptionManager(models.Manager):
def my_filter(self):
# You'd want to make this a smaller query most likely
subscriptions = Subscription.objects.all()
results = []
for subscription in subscriptions:
sub_history = subscription.history_set.first()
if sub_history.end_datetime > timezone.now:
results.append(subscription)
return results
class History(models.Model):
subscription = models.ForeignKey(Subscription)
end_datetime = models.DateTimeField(db_index=True, blank=True, null=True)
objects = SubscriptionManager()
Then: queryset = Subscription.objects().my_filter()
Not a copy-pastable answer, but shows the use of Managers. Given the specificity of what you're looking for, I don't think there's a way to get it just via the plain filter() and exclude().
Without knowing what your end goal here is, it's hard to say whether this is feasible, but have you considered adding a property to the subscription model that indicates whatever you're looking for? For example, if you're trying to get everyone who has a subscription that's ending:
class Subscription(models.Model):
#property
def ending(self):
if self.end_datetime > timezone.now:
return True
else:
return False
Then in your code: queryset = users.filter(subscription_ending=True)
I have tried django's all king of expressions(aggregate, query, conditional) but was unable to solve the problem so i went with RawSQL and it solved the problem.
I have used the below SQL to select the first row and then compare the end_datetime
SELECT (end_datetime > %s OR end_datetime IS NULL) AS result
FROM subscription_history
ORDER BY start_datetime DESC
LIMIT 1;
I will select my answer as accepted if not found a solution with queryset filter chaining in next 2 days.

query the database using django object Q to the same field with the operator &

I apologize in advance if my question has already been there, but I have not found.
there is a model:
class Artikul_cabinets(models.Model):
artikul_cabinets = models.CharField(verbose_name="Артикул шкафа", max_length=20)
title_cabinets = models.CharField(verbose_name="Описание шкафа", max_length=200)
width_cabinets = models.ManyToManyField(Width_cabinets)
depth_cabinets = models.ManyToManyField(Depth_cabinets)
unit_cabinets = models.ManyToManyField(Unit_cabinets)
weight_cabinets = models.ManyToManyField(Weight_cabinets)
type_cabinets = models.ForeignKey(Type_cabinets, default=1)
color_cabinets = models.ForeignKey(Color_cabinets)
glass_cabinets = models.ManyToManyField(Glass_cabinets)
class Meta:
verbose_name_plural = "Артикул шкафа"
def __str__(self):
return self.artikul_cabinets
It is necessary to make the selection on the field
glass_cabinets = models.ManyToManyField(Glass_cabinets)
The selection is done as follows
data = Artikul_cabinets.objects.filter(Q(glass_cabinets=perf) &
Q(glass_cabinets=glass)
perf and glass the variables with different values.
And I returned to my empty QuerySet, although the database element with the parameters 'perf' and 'glass' are present in the record.
Tell me what I'm doing wrong.
also tried:
data = Artikul_cabinets.objects.filter(Q(glass_cabinets=perf),
Q(glass_cabinets=glass)
and also did not work, though if you put the operator '|' the conditions or work out correctly.
So I think you should do Artikul_cabinets.objects.filter(glass_cabinets=perf).filter(glass_cabinets=glass‌​)
check How to filter model results for multiple values for a many to many field in django