how to update more than one database table within one modeladmin form in wagtail? - wagtail

In my question, there are three related models:
class DicSoftware(index.Indexed, ClusterableModel):
name = models.CharField("软件名称", max_length=255, blank=True)
version = models.CharField("版本号", max_length=255, blank=True)
panels = [MultiFieldPanel([
FieldPanel('name', classname="col10"),
FieldPanel('version', classname="col10"),
], "模拟软件")]
class Simulation(index.Indexed, ClusterableModel):
name = models.CharField("算例名称", max_length=255, blank=True)
software = ParentalManyToManyField('DicSoftware', related_name='模拟软件')
panels = [ MultiFieldPanel([
FieldPanel('name', classname="col10"),
FieldPanel('software', classname="col10"),
], "算例")]
With these two models above, wagtail automatically generate table simulation_software with three fields, id, simulation_id, dicsoftware_id. However, I want to add other two fields in table simulation_software, inputFile and inputFilePath. The model of the final table simulation_software should be:
class SimulationSoftware(index.Indexed, ClusterableModel):
simulation = models.ForeignKey(Simulation, verbose_name="算例", help_text="/admin/home/simulation/", on_delete=models.CASCADE, blank=True, null=True, related_name='+')
software = models.ForeignKey(DicSoftware, verbose_name="模拟软件", help_text="/admin/home/dicsoftware/", on_delete=models.CASCADE, blank=True, null=True, related_name='+')
#把读入的文件内容存入inputJSON
inputFile = models.FileField("初始化参数文件", upload_to="files", default="")
outputFilePath = models.CharField("结果文件存储位置", max_length=255, blank=True)
panels = [MultiFieldPanel([
FieldPanel('simulation', classname="col10"),
FieldPanel('software', classname="col10"),
FieldPanel('inputFile', classname="col10"),
FieldPanel('outputFilePath', classname="col10"),
], "算例输入")]
When users add one simulation, they have to give the information as follows:
simulation name.
specify the software(one or more software) used in this simulation.
In one simulation, there can be one or more sofware. If two software are used in this simulation, users should give these information at the same time:
the first sofware's inputFile and outputFilePath.
the second sofware's inputFile and outputFilePath.
How to manage the models' structure and the input panels for inputFiles and outputFilePaths of the software(one or more software).
Anyone can give me some sugguestions? Wish for your help. Thank you very much!

you can relate the sub-models by adding attribute (page) like this:
page = ParentalKey('SimulationSoftware', related_name='<sub-class>_model', on_delete=models.CASCADE)
in each of subclasses (DicSoftware and Simulation)
and then in the content_panels of main clustarable model, use the related_name attributes of each sub-class: FieldPanel("<sub-class>_model", classname="col10"),

Related

How to link Wagtail and Django models together such that Wagtail field can be display in Django model admin

I am working on job application form functionality.
I have a Wagtail job description page with a link to application form.
So far I have built JobDetailPage model in Wagtail and JobAppliction Django model (as below):
class JobDetailPage(Page):
...
apply_form = models.ForeignKey(
JobAppliction,
on_delete=models.SET_NULL,
null=True)
job_title = models.CharField(max_length=100, blank=False, null=True)
...
class JobAppliction(models.Model):
# job_id = models.ForeignKey(
# 'careers.JobDetailPage',
# blank=False, null=True,
# on_delete=models.SET_NULL,
# related_name='position', )
name = models.CharField(
max_length=255, blank=False, null=True)
email = models.EmailField(blank=False, null=True)
...
The same application form can be used by a number of job offers.
How do I link these two models together such that in the admin I can tell which instance of job application was submitted which position?
My admin model is as below:
class JobApplictionAdmin(ModelAdmin):
model = JobAppliction
menu_label = "Applications"
list_display = ('title', 'email')

model relationship design for Blog app in Wagtail site

Below models.py to build a blog in wagtail site is from this post.
class BlogPage(Page):
description = models.CharField(max_length=255, blank=True,)
content_panels = Page.content_panels + [FieldPanel("description", classname="full")]
class PostPage(Page):
header_image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
tags = ClusterTaggableManager(through="blog.PostPageTag", blank=True)
content_panels = Page.content_panels + [
ImageChooserPanel("header_image"),
InlinePanel("categories", label="category"),
FieldPanel("tags"),
]
class PostPageBlogCategory(models.Model):
page = ParentalKey(
"blog.PostPage", on_delete=models.CASCADE, related_name="categories"
)
blog_category = models.ForeignKey(
"blog.BlogCategory", on_delete=models.CASCADE, related_name="post_pages"
)
panels = [
SnippetChooserPanel("blog_category"),
]
class Meta:
unique_together = ("page", "blog_category")
#register_snippet
class BlogCategory(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=80)
panels = [
FieldPanel("name"),
FieldPanel("slug"),
]
def __str__(self):
return self.name
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
class PostPageTag(TaggedItemBase):
content_object = ParentalKey("PostPage", related_name="post_tags")
#register_snippet
class Tag(TaggitTag):
class Meta:
proxy = True
I am wondering, what are the major reasons to introduce extra Intermediary model (class PostPageBlogCategory(models.Model): & class PostPageTag(TaggedItemBase):) to link PostPage to Category & Tag?
Why not just simply use ParentalForeignkey or ParentalManyToManyKey ?
The short answer is that Django's ManyToManyField has some limitations in how the relationship needs to be built when models on either end to not yet exist, adding an 'intermediary model' helps to work around this.
Longer Answer
As with anything in software, there are multiple ways to do something and each has its own pros & cons. Django's built in field is simple and lets you do lots of powerful things to represent relational data but that simplicity comes with some reduced flexibility in how these relationships are managed.
One of the main goals of the Wagtail CMS Admin interface is the ability to work as though the data has been created (including relationships) before actually clicking 'save'. While this may seem simple at first glance, getting to that point requires a bit of nuance under the hood once you start to consider relational data.
Wagtail comes built in with a very powerful library called django-modelcluster which has been purpose built for many of the cases where you want to work with relational data without having all the bits in the DB first.
Each Wagtail Page actually inherits the modelcluster.models.ClusterableModel, which is why some of the features in the blog post seem to work in the editor, even when the DB entries have not yet been saved.
On the blog post you linked, there is a section towards the end with the heading 'ParentalKey' that further explains this nuance and how just using Django's basic approach has some draw backs.
On the Django docs for many-to-many relationships, have a read through and note that each individual model instance must be in the database first and only then can you 'link' the two instances with a second update on each.

Model needs to have a value for field "id" before this many-to-many relationship can be used

I am unable to save a model in Django with a ManyToManyField while inside Django Admin because I don't know how to create the ID in the database first, and then save the data. It's trying to do both at the same time, but I have to separate the two processes, and I don't know how.
I've gone five full pages deep on Google, and there are tons of answers, but nothing that I can relate to my specific use case.
The error message I get when submitting the form that has the ManyToManyField in it:
ValueError: "<Clients: Lowe, Robb>" needs to have a value for field "id" before this many-to-many relationship can be used.
Here's the model I'm using:
class BusinessInfo(models.Model):
id = models.IntegerField(primary_key=True, editable=False)
business_name = models.CharField(blank=True, null=True, max_length=50)
business_address = models.CharField(blank=True, null=True, max_length=50)
business_city = models.CharField(blank=True, null=True, max_length=50)
business_state = models.CharField(choices=STATE_SELECT, null=True, blank=True, max_length=50)
related_person = models.ManyToManyField('Clients', related_name='related_person', null=True, blank=True)
business_zip = models.IntegerField(blank=True, null=True)
business_contact_name = models.CharField(blank=True, null=True, max_length=50)
business_phone = PhoneField(null=True, help_text='Phone Number | <span style="color: red;">Required</span>')
business_email = models.CharField(blank=True, null=True, max_length=50)
And specifically the field I created:
related_person = models.ManyToManyField('Clients', related_name='related_person', null=True, blank=True)
I've also attempted to use django-modelcluster to no avail.
I'm pretty new to Django so if anyone can help me out and use explicit code that would help me a great deal. Thank you!

Inline creation of snippet in a streamfield block (Wagtail 2.3+)

so lets say I have the following models set up for Wagtail:
#register_snippet
class MySnippet(models.Model):
name = models.CharField(max_length=200, null=True)
panels = [FieldPanel('name'),]
def __str__(self):
return self.name
class Meta:
ordering = ['name',]
class MyPage(Page):
body = StreamField([
('mysnippet', SnippetChooserBlock(required=False, label='MySnippet', target_model='MySnippet')),
], blank=True, help_text='')
content_panels = Page.content_panels + [
StreamFieldPanel('body', heading='Stuff to add'),
]
My client will be creating a lot of MySnippet items as they go. It's going to be super awkward for them to move to another view in their CMS, create a MySnippet, then come back to their main MyPage editor to choose it.
Q1 Is there a simple way to add a SnippetChooseOrInlineCreate() block so clients can add new MySnippets as they create MyPages?
Q2 If there's no existing simple way, how would you recommend approaching this?

Django models - how to create a selected_instance field from an instance in a collection

Django noob questions:
I want to create a site which allows users to share info about cars. Each car should have a collection of images, and the submitter should select one of the images to be used to represent the car on a listing page. A basic set of models is shown below:
class Manufacturer(models.Model):
name = models.CharField(max_length=255)
class ModelBrand(models.Model):
name = models.CharField(max_length=255)
class Car(models.Model):
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
# identifying information
manufacturer = models.ForeignKey(Manufacturer)
model_brand = models.ForeignKey(ModelBrand)
model_year = models.PositiveIntegerField()
class CarImage(models.Model):
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
car = models.ForeignKey(Car, related_name='images')
source_url = models.CharField(max_length=255, blank=True)
image = ImageField(upload_to='cars')
But how do I model the selected image? Do I put a 'selected' BooleanField on the CarImage class? And how do I configure the Car and CarImage admin classes to allow an admin site user to select and image for a car from its 'images' collection?
First, I would like to suggest you to refactor your class using an auxiliary TimeStampedClass
class TimeStampedModel(models.Model):
"""
Abstract class model that saves timestamp of creation and updating of a model.
Each model used in the project has to subclass this class.
"""
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
class Meta:
abstract = True
ordering = ('-created_on',)
So you can use this class over your project, subclassing it.
One simple solution for your question is attach your image gallery to your car, and create one attribute that is a IntegerField that stores the picture position in the image gallery:
...
class CarImage(TimeStampedField):
source_url = models.CharField(max_length=255, blank=True)
image = ImageField(upload_to='cars')
class Car(TimeStampedModel):
image_gallery = models.ManyToManyField(CarImage)
selected_picture = models.IntegerField(default=0)
# identifying information
manufacturer = models.ForeignKey(Manufacturer)
model_brand = models.ForeignKey(ModelBrand)
model_year = models.PositiveIntegerField()
So, if selected_picture is n, you just need to get n-th picture inside image_gallery

Resources