Colander subclassing SchemaNode - python

I wonder if someone can help me. I'm explore deform and colander in a new project and was following the documentation about subclassing SchemaNode. However, whilst the documentation states that
subclass can define the following methods and attributes: preparer,
validator, default, missing, name, title, description, widget, and
after_bind.
when I define title, it doesn't seem to come through. Here is some example code that I'm using:
class LocationSchemaNode(colander.SchemaNode):
schema_type = colander.Int
title = 'Location'
missing = None
validator = colander.Range(
min=1,
min_err='Please select a valid location'
)
class PersonSchema(colander.Schema):
location_id = LocationSchemaNode()
However, when the form is rendered the label for the field is "Location Id" not "Location" as per the title defined in SchemaNode. If instead I write:
class PersonSchema(colander.Schema):
location_id = LocationSchemaNode(title="Location")
Then all appears as I want, but the documentation seems to state I don't need to do this, and if I do it kind of defeats the point of pre-defining a SchemaNode if I have to keep defining fields.
Am I missing something, or is deform doing something that it shouldn't be (I doubt that is going to be the case). Any help is much appreciated.
Keith

This appears to be a bug that was fixed: https://github.com/Pylons/colander/pull/183
Also, the patch seems to be in the latest available release of colander so an update to the latest version should fix this issue.
Correction:
The example given in that PR exactly matches this question, but the fix given didn't actually fix that exact issue! So, I filed another PR to fix that issue and used the example given in #183 as the test. You can manually patch your own copy if you can't wait for the fix to be introduced into the repo or the next release.

Related

How can I override __str__ in models.py?

Apologies if this is a silly question, I am pretty new to python and django.
I am following along with a django tutorial, and we are creating a fake movie site that lists movies by genre, title, etc.
I am currently trying to override the __str__ function in the models.py file, so rather than displaying Genre (1), it displays the actual genre (ex. "Action").
Here's how my models.py looks currently:
from tkinter import CASCADE
from django.db import models
from django.utils import timezone
# Create your models here.
class Genre(models.Model):
name = models.CharField(max_length=255)
def __str__ (self):
return self.name
class Movie(models.Model):
title = models.CharField(max_length=255)
release_year = models.IntegerField()
number_in_stock = models.IntegerField()
daily_rate = models.FloatField()
genre = models.ForeignKey(Genre, on_delete=models.CASCADE)
date_created = models.DateTimeField(default=timezone.now)
However, vscode is underlining the
def __str__ (self):
When I hover over it, it tells me:
str does not return str pylint(E0307: invalid -str- returned
I tried looking at other solutions, but I could not find one that seemed to match my scenario, so I do apologize if this has been solved elsewhere, and I am too incompetent to understand the problem.
Thanks for your patience!
This question has a couple of good illustrations of why it's important to understand your code before you rely too heavily on IDEs and tools like pylint. I've two suggestions:
The pylint error is just a warning to indicate that your code might have a problem - it is not an absolute fact. The E0307 error says:
Used when a __str__ method returns something which is not a string.
It would be more accurate if it had said "when the pylint checker cannot be sure that you're returning a string". In this case, it's because it doesn't recognise that a Django CharField will in fact return a valid string instance. Your existing __str__ method would work perfectly fine if you ran the code - regardless of what pylint thinks.
You can work around this by forcing the return value to look like a string, e.g., with:
return str(self.name)
or
return f"{self.name}"
But it would be equally valid to disable the check for this line in pylint, with the understanding of why it reported the error. Just applying a fix found on Stack Overflow without understanding the issue is going to make it hard to debug your code in future.
There is a second, completely unrelated issue in your code which will cause you problems later, and that is this line:
from tkinter import CASCADE
I am pretty confident that this has been inserted by your IDE, without your explicitly adding it, because you tried to use CASCADE in your ForeignKey. This is the IDE trying to be helpful - unfortunately it has imported something completely useless, that can cause you problems when you try to deploy your code.
Both of these highlight an important principle: don't rely on IDEs or linters. They are not a substitute for understanding your own code.

#api. depends or #api.onchange not working with compute field. why?

In CRM module , in pipeline record, I have added one Float called price_difference with the help of inheritance. That field is difference between planned_revenue and sale_amount_total fields from the CRM module.
when I tired to use method name get_price_diff(), it is not worked for me. I want to use sale_amount_total and planned_revenue with #api. depends or #api.onchange, but it is not working now.
Working of my method is simple, it depending upon sale_amount_total and price_difference field. If any of the values changed, the method should run.
sale_amount_total is compute field in base module.
My code is below.
How to do it?
class rate_record(models.Model):
_inherit = 'crm.lead'
price_difference = fields.Float(string='Price Difference', readonly=True)
#api.onchange('sale_amount_total', 'planned_revenue')
def get_price_diff(self):
self.price_different = self.planned_revenue - self.sale_amount_total
What you want to do can't be achieved using api.depends or api.onchange only because it's not the way they work (see: HERE).
You can do it by using a compute method:
class rate_record(models.Model):
_inherit = 'crm.lead'
price_difference = fields.Float(
string='Price Difference',
compute='get_price_diff')
#api.depends('sale_amount_total', 'planned_revenue')
def get_price_diff(self):
self.price_different = self.planned_revenue - self.sale_amount_total
I can only guess, because to this moment i don't have all information about your code. The onchange method get_price_diff looks right. But you set the field as readonly=True, and that means, the "computed" value won't be saved.
Since Odoo 11 you can force Odoo to save value changes on readonly marked fields with force_save="1" on it's field view definition:
<field name="price_difference" force_save="1" />
For older versions there are modules in the OCA repositories on Github. For example for Odoo 10 web_readonly_bypass.
Your code:
price_difference = fields.Float(string='Price Difference', readonly=True)
readonly=True - you won't be able to save this data price_difference.
Remove it, and test your code. Should work.
I had the same problem, it updates, but when I save it, it just disappears. Not sure if this is your problem, because your question is vague (what do you mean its not working now? Not saving the data? If this is the case, then my solution will work for you as it worked for me)

SQLAlchemy Query - Dynamic class selection

I can find many examples of how to build these queries where the 'filter_by' part is dynamic, but I cant seem to find anything where the class name is the dynamic bit. I'm sure an answer must be out there, just not sure what to search for.
My use case it this: I need to build a dynamic SQLAlchemy query. The twist is that its the class name changing rather than the filter variables. The query type will always be a '.get()' so I'm good there. I should also say that simply plugging a variable in where the class name should be doesnt work.
db_model = request.values.get("db_model_class")
item_id = request.values.get("item_id")
result = db.session.query(db_model).get(int(item_id))
How do I go about making this work?
Create a lookup of relevant classes:
models = {"Foo": Foo,
"Bar": Bar,
"...": ...}
and get the class:
db_model = models[request.values.get("db_model_class")]
db_model = request.values.get("db_model_class")
item_id = request.values.get("item_id")
Maybe the problem is here.
you need to set a debug point.
the answer by Ilja Everilä was great. so you need to check the way you get the parameters.
if you are using a pycharm, configure a debug setting.
if you are using Macbook,
click the run->edit configurations->
then you click the left margin of the code, and restart your app with the debug button on the top right corner. when you request the url again, you will see what your view function has get in the request object.

Duplicate/Archive entry in Django to another model

I have been at this for Two days and I'm now hoping someone can point me in the right direction. All I am trying to do is duplicate an entry in a table/model into another model with mirrored fields, essentially creating an archived version. I want this to happen when the user calls the update view.
What I have tried so far is setting pk to None and then trying to find a way to move the previous version to the mirrored/archive model. After a couple of hours of research I gave up on this path. Next I thought the answer would lie with the pre_save receiver but I can't find a way to access the model instance to then save that to the archive model.
#receiver(pre_save, sender=InstrumentAnnual)
def archive_calc_instance(sender, instance, **kwargs):
stored_id = getattr(instance, 'id', None)
e = InstrumentAnnual.objects.filter(id = stored_id)
archive = InstrumentAnnualArchive(e.field_name, e.another_field_name...)
archive.save()
As far as I can tell this should work however e only contains the First field from the model.
Is there something that can be done with this code to achieve my goal or is there a more 'Django' way to solve this? I.e. some sort of official archive feature?
Thanks in advance.
With the help of #Igor's comment I amended my solution to this:
def archive_calc(self, object_id):
annual = InstrumentAnnual.objects.get(id = object_id)
annual_archive = InstrumentAnnualArchive()
for field in annual._meta.fields:
setattr(annual_archive, field.name, getattr(annual, field.name))
annual_archive.pk = None
annual_archive.save()
It occured to me that using pre_save wouldn't work as it is listening/linked to a model, not a view as I originally thought. So I placed the above code in my Update View and called it passing the id in object_id.
Thanks again for the help.
You should be using named arguments in your constructor, otherwise the first argument will be interpreted as the id, so try:
# omitted code
e = InstrumentAnnual.objects.filter(id=stored_id)
archive = InstrumentalAnnualArchive(field_name=e.field_name, another_name=e.another_field_name, …)
archive.save()
But you could also use Django's create function, so:
# omitted code
e = InstrumentAnnual.objects.filter(id=stored_id)
archive = InstrumentalAnnualArchive.objects.create(field_name=e.field_name, another_name=e.another_field_name, …)
This way handles the save for you, so you don't need to explicitly save your object.

How do I get the key value of a db.ReferenceProperty without a database hit?

Is there a way to get the key (or id) value of a db.ReferenceProperty, without dereferencing the actual entity it points to? I have been digging around - it looks like the key is stored as the property name preceeded with an _, but I have been unable to get any code working. Examples would be much appreciated. Thanks.
EDIT: Here is what I have unsuccessfully tried:
class Comment(db.Model):
series = db.ReferenceProperty(reference_class=Series);
def series_id(self):
return self._series
And in my template:
more
The result:
more
Actually, the way that you are advocating accessing the key for a ReferenceProperty might well not exist in the future. Attributes that begin with '_' in python are generally accepted to be "protected" in that things that are closely bound and intimate with its implementation can use them, but things that are updated with the implementation must change when it changes.
However, there is a way through the public interface that you can access the key for your reference-property so that it will be safe in the future. I'll revise the above example:
class Comment(db.Model):
series = db.ReferenceProperty(reference_class=Series);
def series_id(self):
return Comment.series.get_value_for_datastore(self)
When you access properties via the class it is associated, you get the property object itself, which has a public method that can get the underlying values.
You're correct - the key is stored as the property name prefixed with '_'. You should just be able to access it directly on the model object. Can you demonstrate what you're trying? I've used this technique in the past with no problems.
Edit: Have you tried calling series_id() directly, or referencing _series in your template directly? I'm not sure whether Django automatically calls methods with no arguments if you specify them in this context. You could also try putting the #property decorator on the method.

Categories