Django CMS - Copy relations in custom plugin not working - django-cms

I have a problem with copy_relations after publishing a page.
I have a plugin with additional model. This model has ForeignKey to my plugin.
class InfContactForm(CMSPlugin):
name = models.CharField(max_length=50)
def copy_relations(self, oldinstance):
self.inf_contact_form.all().delete()
for inf_contact_form in oldinstance.inf_contact_form.all():
inf_contact_form.pk = None
inf_contact_form.plugin = self
inf_contact_form.save()
class InfContactFormAgreement(models.Model):
inf_contact_form = models.ForeignKey(InfContactForm, related_name="inf_contact_form")
agreement = HTMLField(blank=True, null=True)
The "InfContactFormAgreement" model is then used as stacked inline in "InfContactForm" plugin form.
Like it is written in docs, there is also copy_relations() method but when the page is published, nothing happens. When I get back again to edit mode InfContactFormAgreement is doubled every time I do it.
Here is also my cms_plugins.py file:
class PluginInfContactForm(CMSPluginBase):
render_template = '_contact_form.html'
name = name1
model = InfContactForm
require_parent = False
inlines = [AgreementStackedInline, ]
def render(self, context, instance, placeholder):
context['instance'] = instance
context = super(PluginInfContactForm, self).render(context, instance, placeholder)
agreements = instance.inf_contact_form.all()
context.update({
'agreements': agreements,
})
return context
plugin_pool.register_plugin(PluginInfContactForm)
Thank you for any advice.

instead of:
inf_contact_form.plugin = self
you should use:
inf_contact_form.inf_contact_form = self
and the resulting code should look like this:
def copy_relations(self, oldinstance):
self.inf_contact_form.all().delete()
for inf_contact_form in oldinstance.inf_contact_form.all():
inf_contact_form.pk = None
inf_contact_form.inf_contact_form = self
inf_contact_form.save()
regards

I had a similar situation, the only difference being that my many-to-many relationship was not in a plugin, but an extension (PageExtension).
In my case, #Dariusz solution did not work, and I had to update the implicit "inbetween" model that exists between the Extension model and the Associated model.
My solution (the key being the "through"):
class Extension(PageExtension):
tags_regions = models.ManyToManyField(Region, related_name="articles", blank=True)
def copy_relations(self, oldinstance):
for region_tag in oldinstance.tags_regions.through.objects.filter(extension=oldinstance):
region_tag.extension = self
region_tag.save()

Related

Tortoise pydantic_model_creator method not contain ForeignKeyField of a model

I have two models Article and Tag. Article has a foreign key from Tag
class Tag(MyAbstractBaseModel):
name = fields.CharField(max_length=255, index=True)
class Article(MyAbstractBaseModel):
title = fields.CharField(max_length=255, index=True)
body = fields.CharField(max_length=255)
tag = fields.ForeignKeyField(model_name="Tag", related_name='article', on_delete=fields.CASCADE)
Here I would like to have my response checked by the following
#router.get("/article", response_model=get_serialize_pydantic(Article))
async def get():
pass
Here is how I get the response model
from tortoise.contrib.pydantic import pydantic_model_creator
from pydantic import BaseModel, Field, create_model
def get_serialize_pydantic(models_obj: Type[Model], exclude: Tuple[str, ...] = None):
my_model_pydantic = pydantic_model_creator(models_obj)
response_pydantic = create_model(
__model_name=f"{models_obj.__name__}_response_pydantic",
data=(Optional[List[my_model_pydantic]], Field(None, title="data")),
__base__=BaseResponse)
return response_pydantic
Problem is I only title and body in my response model, there is no tag in it. Anyone knows why?
This is not an issue from pydantic side. It's pydantic_model_creator of tortoise that causes the problem.
The solution is simple. Just init before serialisation.
from tortoise import Tortoise
Tortoise.init_models(["__main__"], "models")
my_model_pydantic = pydantic_model_creator(models_obj)
https://tortoise-orm.readthedocs.io/en/latest/contrib/pydantic.html#relations-early-init

Python dictionary not adding new keys

I have checked a few of the posts having the same issue and it tells me how to do it but it is not working.
My code:
class Product:
def __init__(self,price,prod_id,quantity):
self.price = price
self.prod_id = prod_id
self.quantity = quantity
self.shop_inventory = {}
self.amount = {}
class Inventory(Product):
def update_stock(self):
self.shop_inventory[self.prod_id] = self.quantity
self.amount[self.prod_id] = self.price
def return_stock_item(self):
look_up = input("What item would you like to check? ")
item = self.shop_inventory[look_up.lower()]
price = self.amount[look_up.lower()]
return f"{look_up.capitalize()}: There are {item} in stock and cost ${price} each."
def display_stock(self):
return self.amount.items()
To set the values Ive been using
stock = Inventory(2.34,'apple',5)
stock.update_stock()
It's part of a class and everything else in the class is working right. Just this one function is not working. I have tried this method as well as the update() method and neither are adding new key values. It is only over writing the same first spot. I have an item check in the class for all the dictionary items and it always only returns one [key][value] no matter how many times I change the key name.
Thank you for any help.

Mongoengine serialize dictionary (with nested dicts)?

I've created a dictionary from an Uploaded file in Django.
This dictionary has a nested list of dictionaries:
file = {"name": "filename", "sections": [{"section_name": "string", "lines": [{line_number: 0, "line"; "data"}]}], "etc": "etc"}
The model represents the dictionaries depth too.
class Line(EmbeddedDocument):
line_number = IntField()
line = StringField()
definition = ReferenceField(Definition)
class Section(EmbeddedDocument):
section_name = StringField()
lines = EmbeddedDocumentListField(Line))
class File(Document):
name = StringField()
sections = EmbeddedDocumentListField(Section))
created_on = DateTimeField()
created_by = StringField()
modified_on = DateTimeField()
modified_by = StringField()
In the POST I have the following to chop the file up into the above Dict (the file is a simple text file):
file= {}
with open(os.path.join(path, filename + ".txt"), 'r') as temp_file:
filelines = temp_file.readlines()
sections = []
section = {}
lines = []
for i, l in enumerate(filelines):
if i == 0:
section["section_name"] = "Top"
elif '*' in l:
if l.index('*') == 0 and '*' not in lines[len(lines) - 2"line"]:
section["lines"] = lines
lines = []
sections.append(section)
section = dict()
section["section_name"] = filelines[i + 1][1:-2]
line = {"line_number": i + 1, "line": l}
lines.append(line)
section['lines'] = lines
sections.append(section)
file["name"] = filename
file["sections"] = sections
I will tidy this up eventually.
Once the dict has been made how do I serialise it using the serializer?
Is it possible to insert this into a serializer?
If not how can I get it all into the database with validation?
I've tried json.dumps() and JsonRequst() then putting them in data= for the serializer but get Unable to get repr for <class '....'>
I'm pretty new to Django and MongoDB so if you need more info I can provide :)
Thanks!
Update
Change the model's List Fields to EmbeddedDocumentListField as suggest in the answer.
Answered
Thanks to Boris' suggestion below it pointed me to an error I wasn't getting initially. I had a typo and passing the dict directly into FileSerializer(data=file) works like a charm! :)
James!
The easiest way to validate that your incoming JSONs adhere to the Mongoengine Documents schema that you've specified is to use DRF-Mongoengine's DocumentSerializer.
Basically, what you need to do is create a serializer
serializers.py
import rest_framework_mongoengine
class FileSerializer(rest_framework_mongoengine.DocumentSerializer):
class Meta:
fields = '__all__'
model = File
Then you need a view or viewset that makes use of this Serializer to respond to GET/POST/PUT/DELETE requests.
views.py
from rest_framework_mongoengine import viewsets
class FileViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
serializer_class = FileSerializer
def get_queryset(self):
return File.objects.all()
and register this viewset with a router
urls.py
from rest_framework import routers
# this is DRF router for REST API viewsets
router = routers.DefaultRouter()
# register REST API endpoints with DRF router
router.register(r'file', FileViewSet, r"file")
I'd also recommend using EmbeddedDocumentListField instead of ListField(EmbeddedDocumentField(Section)) - it has additional methods.

Flask-WTF: Queries of FormFields in FieldList are none after validate_on_submit

I'm trying to generate dynamic forms using Flask-WTF to create a new product based on some templates. A product will have a list of required key-value pairs based on its type, as well as a list of parts required to build it. The current relevant code looks as follows:
forms.py:
class PartSelectionForm(Form):
selected_part = QuerySelectField('Part', get_label='serial', allow_blank=True)
part_type = StringField('Type')
slot = IntegerField('Slot')
required = BooleanField('Required')
def __init__(self, csrf_enabled=False, *args, **kwargs):
super(PartSelectionForm, self).__init__(csrf_enabled=False, *args, **kwargs)
class NewProductForm(Form):
serial = StringField('Serial', default='', validators=[DataRequired()])
notes = TextAreaField('Notes', default='')
parts = FieldList(FormField(PartSelectionForm))
views.py:
#app.route('/products/new/<prodmodel>', methods=['GET', 'POST'])
#login_required
def new_product(prodmodel):
try:
model = db.session.query(ProdModel).filter(ProdModel.id==prodmodel).one()
except NoResultFound, e:
flash('No products of model type -' + prodmodel + '- found.', 'error')
return redirect(url_for('index'))
keys = db.session.query(ProdTypeTemplate.prod_info_key).filter(ProdTypeTemplate.prod_type_id==model.prod_type_id)\
.order_by(ProdTypeTemplate.prod_info_key).all()
parts_needed = db.session.query(ProdModelTemplate).filter(ProdModelTemplate.prod_model_id==prodmodel)\
.order_by(ProdModelTemplate.part_type_id, ProdModelTemplate.slot).all()
class F(forms.NewProductForm):
pass
for key in keys:
if key.prod_info_key in ['shipped_os','factory_os']:
setattr(F, key.prod_info_key, forms.QuerySelectField(key.prod_info_key, get_label='version'))
else:
setattr(F, key.prod_info_key, forms.StringField(key.prod_info_key, validators=[forms.DataRequired()]))
form = F(request.form)
if request.method == 'GET':
for part in parts_needed:
entry = form.parts.append_entry(forms.PartSelectionForm())
entry.part_type.data=part.part_type_id
entry.slot.data=slot=part.slot
entry.required.data=part.required
entry.selected_part.query = db.session.query(Part).join(PartModel).filter(PartModel.part_type_id==part.part_type_id, Part.status=='inventory')
if form.__contains__('shipped_os'):
form.shipped_os.query = db.session.query(OSVersion).order_by(OSVersion.version)
if form.__contains__('factory_os'):
form.factory_os.query = db.session.query(OSVersion).order_by(OSVersion.version)
if form.validate_on_submit():
...
Everything works as expected on a GET request, but on the validate_on_submit I get errors. The error is that all of the queries and query_factories for the selected_part QuerySelectFields in the list of PartSelectionForms is none, causing either direct errors in WTForms validation code or when Jinja2 attempts to re-render the QuerySelectFields. I'm not sure why this happens on the POST when everything appears to be correct for the GET.
I realized that although I set the required queries on a GET I'm not doing it for any PartSelectionForm selected_part entries on the POST. Since I already intended part_type, slot, and required to be hidden form fields, I added the following immediately before the validate_on_submit and everything works correctly:
for entry in form.parts:
entry.selected_part.query = db.session.query(Part).join(PartModel).\
filter(PartModel.part_type_id==entry.part_type.data, Part.status=='inventory')

Conditional classname and text (Rails)

I'm pretty new to Rails and trying some basic stuff like conditional classes.
On the 'show' view I have an element that changes styling depending on the stock availability, but also the text changes accordingly.
People keep saying the controller should be as small as possible, but placing this conditional in the view also feels dirty. Is this really the best way?
Current controller:
def show
#tyre = Tyres::Tyre.find_by_id(params[:id])
if #tyre.in_stock
#availability = I18n.t("products.filter.other.in_stock")
#availability_class = 'i-check-circle color--success'
else
#availability = I18n.t("products.filter.other.not_in_stock")
#availability_class = 'i-cross-circle color--important'
end
end
Edit:
Controller:
def show
#tyre = Tyres::Tyre.find_by_id(params[:id])
if #tyre.in_stock
#availability_append = ".in_stock"
else
#availability_append = ".not_in_stock"
end
#availability = I18n.t("products.filter.other#{#availability_append}")
end
View:
.xs-12.description__status
%i{class: (#tyre.in_stock? ? 'i-check-circle color--success' : 'i-cross-circle color--important')}
= #availability
You can clean your controller tyres_controller.rb (i suppose) method,
def show
#tyre = Tyre.find(params[:id]) # I believe you have a model named 'tyre'
end
Then, there will be a file named tyres_helper.rb in your myproject/app/helpers/. Put the following code there,
def tyre_availability(tyre) # it'll return an array with two values, first one is class name, second one is localized value
if tyre.in_stock
return 'i-check-circle color--success', I18n.t("products.filter.other.in_stock")
else
return 'i-cross-circle color--important', I18n.t("products.filter.other.not_in_stock")
end
end
and, in the view you can use,
.xs-12.description__status
%i{:class => tyre_availability(#tyre)[0]}
= tyre_availability(#tyre)[1]

Resources