Django mptt: model with a mptt tree instance as a field - python

I am developing an app with Django where I have to model systems.
A system is defined by a number of standard fields (name, owner, etc...) and then a tree of assets (implemented using mptt). A given asset can be part of several systems. A node in the tree of assets can point only to a given asset.
So, I have a model to represent the assets (simplified):
class Asset(models.Model):
name = models.CharField(verbose_name = 'name', max_length = 64, unique = True, blank = False, null = False)
I have a class to represent the system trees (simplified):
class System_Tree(MPTTModel):
parent = TreeForeignKey('self',verbose_name = 'parent', on_delete = models.CASCADE, blank = True, null = True, related_name = 'children')
asset = models.ForeignKey(Asset, verbose_name = 'asset', on_delete = models.CASCADE, blank = False, null = False)
I have a class to represent the system (simplified):
class System(models.Model):
name = models.CharField(verbose_name = 'name', max_length = 64, unique = True, blank = False, null = False)
I would like to show in a template the system with its fields and its specific system tree, but I don't know how to relate a specific instance of a system tree (maybe identified by a root node (parent = null)) with the System model.
Should I define a new field type representing system trees?
Should I store only a reference to the parent node id as a big integer field?
Is there any way then to automate the admin interface to show the right forms for a system (the standard fields and then a tree for the system tree)?
Thanks in advance!!

Related

Django Factory for model with Generic Foreign Key

I'm trying to write up a Factory for a model with a GFK for testing but I can't seem to get it working. I've referred to the common recipes in the docs, but my models don't match up exactly, and I'm also running into an error. Here are my models
class Artwork(models.Model):
...
region = models.ForeignKey("Region", on_delete=models.SET_NULL, null=True, blank=True)
class Region(models.Model):
# Could be either BeaconRegion or SpaceRegion
region_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
region_object_id = models.PositiveIntegerField()
region = GenericForeignKey("region_content_type", "region_object_id")
class SpaceRegion(models.Model):
label = models.CharField(max_length=255)
regions = GenericRelation(
Region,
content_type_field="region_content_type",
object_id_field="region_object_id",
related_query_name="space_region",
)
class BeaconRegion(models.Model):
label = models.CharField(max_length=255)
regions = GenericRelation(
Region,
content_type_field="region_content_type",
object_id_field="region_object_id",
related_query_name="beacon_region",
)
Essentially, an Artwork can be placed in one of two Regions; a SpaceRegion or BeaconRegion.
I've created the following Factorys for the corresponding models
class RegionFactory(factory.django.DjangoModelFactory):
region_object_id = factory.SelfAttribute("region.id")
region_content_type = factory.LazyAttribute(
lambda o: ContentType.objects.get_for_model(o.region)
)
class Meta:
exclude = ["region"]
abstract = True
class BeaconRegionFactory(RegionFactory):
label = factory.Faker("sentence", nb_words=2)
region = factory.SubFactory(RegionFactory)
class Meta:
model = Region
class SpaceRegionFactory(RegionFactory):
label = factory.Faker("sentence", nb_words=2)
region = factory.SubFactory(RegionFactory)
class Meta:
model = Region
class ArtworkFactory(factory.django.DjangoModelFactory):
...
region = factory.SubFactory(SpaceRegionFactory)
In my test, I try to create an Artwork using ArtworkFactory(), but it errors with
AttributeError: The parameter 'region' is unknown. Evaluated attributes are {}, definitions are <DeclarationSet: {'region_object_id': <SelfAttribute('region.id', default=<class 'factory.declarations._UNSPECIFIED'>)>, 'region_content_type': <factory.declarations.LazyAttribute object at 0x1068cf430>, 'label': <factory.faker.Faker object at 0x1068cf880>}>
What am I doing wrong here?
The issue comes when resolving ArtworkFactory.region.region, i.e SpaceRegionFactory.region.
From your models, it seems that:
Region is a table which points to either SpaceRegion or BeaconRegion
SpaceRegion and BeaconRegion are simple tables, with a helper to retrieve the related Region object.
The first step in those complex relation chains is to write the code without factories:
>>> shire = SpaceRegion(label="Shire")
>>> shire_generic = Region(region=shire)
>>> the_ring = Artwork(region=shire_generic)
This tells us that the Region is always created after the SpaceRegion or BeaconRegion, giving the following factories:
class SpaceRegionFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.SpaceRegion
label = factory.Faker("sentence", n_words=2)
class RegionFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Region
region = factory.SubFactory(SpaceRegionFactory)
class ArtworkFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Artwork
region = factory.SubFactory(RegionFactory)
With this, you should be able to get your code working.
Note how we're simply setting the region field on Region: Django's internals will extract the object content type / content ID automatically.
Additional options
You could tune the RegionFactory to let callers decide whether they want a SpaceRegion or a BeaconRegion:
class RegionFactory(factory.django.DjangoModelFactory):
class Meta:
models = Region
class Params:
space = True # Request a SpaceRegion
region = factory.Maybe(
factory.SelfAttribute("space"),
factory.SubFactory(SpaceRegion),
factory.SubFactory(BeaconRegion),
)

Django model form field to have a user dropdown list based on a condition

In a Django Modelform (Product_definition), i want to have a dropdown(Merchant name) which will show users only if the their designation in User form is "Merchant".
is it possible that I could get the list of users for the dropdown based on this condition .Please note that i don't require it to be a foreign key as connecting the models is not required.
This is the form which contains the Designation :
from django.contrib.auth.models import User
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete = models.CASCADE)
#extra UserAttribute
MERCHANT = 'MR'
FABRIC = 'FR'
WASHING = 'WS'
PRINT = 'PR'
PLANNER = 'PL'
DESIGNATION_CHOICES =(
(PLANNER,'Planner'),
(MERCHANT,'Merchant'),
(FABRIC,'Fabric'),
(WASHING,'Washing'),
(PRINT,'Printing'),
)
Designation =models.CharField(
max_length = 20,
choices = DESIGNATION_CHOICES,
default= 'PLANNER'
)
def __str__(self):
return self.user.username
and this is the form with Merchant Name where I want the names of all merchants to appear.
class Product_definition(models.Model):
Order_number = models.CharField(max_length=25,unique = True, blank = True, null = True)
style_name = models.CharField(max_length=15, blank = True, null = True)
color = models.CharField(max_length=15, blank = True, null = True)
Order_qty = models.PositiveIntegerField()
SMV = models.FloatField()
MERCHANT = models.ForeignKey(UserProfileInfo,on_delete= models.CASCADE,default='Select')
def __str__(self):
return self.Order_number
I have created a foreign key for now but I don't require it and it doesn't list the names of only the merchant in the drop down.
I think you can do it like this using ModelChoiceField:
class ProductForm(forms.ModelForm): # please use CamelCase when defining Class Names
MERCHANT = forms.ModelChoiceField(queryset=UserProfileInfo.objects.filter(Designation=UserProfileInfo.MARCHENT)) # Please use sname_case when naming attributes
class Meta:
model = Product_definition # Please use CamelCase when defining model class name
fields = '__all__'

Django; requirement dependencies on sets of fields

How could I add rules to a Django Model making certain fields that aren't required by default, required if another field is set. Or even the other way around
Let's say I have this Model:
class Item(models.Model):
name = models.CharField(max_length = 75)
cant_be_sold = models.BooleanField()
flat_price = models.IntegerField(blank = True, null = True, default = None, validators = [MinValueValidator(0)])
defense = models.IntegerField(blank = True, null = True, default = None, validators = [MinValueValidator(0)])
required_classes = models.ManyToManyField('otherappname.Class', related_name = 'Requires_Classes', blank = True, null = True, default = None)
Let's say there can be 2 situations here;
I mark cant_be_sold as True; now flat_price can only be None (NULL)
I fill in defense; now it IS required to select one or more required_classes
I wonder what the nice way to do this in Django is. Would help me a bunch to prevent wrongfully entries, as my Item model has over 70 property fields, due to the great extend of Item Variances in my system.
Write a clean method for your model. In it, you can change field values, and raise validation errors. The following example should get you started.
def clean(self):
if self.cant_be_sold and self.flat_price is not None:
raise ValidationError("flat_price must be None when cant_be_sold is True")

Abstracting Exisiting Django Models

I've got two models in a Django application that have exactly the same fields, but different types of information is stored in each.
For example:
class A(models.Model)
field_a = models.CharField(primary_key = True, max_length = 24)
field_b = models.CharField(primary_key = True, max_length = 24)
class B(models.Model)
field_a = models.CharField(primary_key = True, max_length = 24)
field_b = models.CharField(primary_key = True, max_length = 24)
It seems like it would make sense to contain these in an abstract model and have these two classes as sub-classes. I was assuming that I could simply do this, without needing to make DB modifications, but Django isn't able to find the fields of my models any longer.
Could someone offer advice?
If you create a new abstract class this won't interfere with your database. As you can see in documentation https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes abstract classes are just python classes without database impact.
your code could be looks like this:
class Parent(models.Model)
field_a = models.CharField(primary_key = True, max_length = 24)
field_b = models.CharField(primary_key = True, max_length = 24)
class Meta:
abstract = True
class A(Parent)
pass
class B(Parent)
pass

Blank form fields are not accepted

Table in PostgreSQL database:
CREATE TABLE pmss_recordmodel
(
id serial NOT NULL,
"Name" character varying(100),
CONSTRAINT pmss_recordmodel_pkey PRIMARY KEY (id)
)
WITH (OIDS=FALSE);
ALTER TABLE pmss_recordmodel OWNER TO postgres;
Model:
class RecordModel(models.Model):
def __unicode__(self):
return self.Name
Name = models.CharField(max_length = 100, unique = False, null = True, blank = True)
When I POST data with blank Name field, form.is_valid() returns False. Why? Am I missing something?
EDIT:
class RecordForm(forms.ModelForm):
class Meta:
model = RecordModel
Name = forms.CharField(label = "Имя ", widget = forms.TextInput(attrs = {'size': 15}))
Django forms documentation:
By default, each Field class assumes the value is required
Set the "required" argument to False as such:
Name = forms.CharField(required = False, label = "Имя ", widget = forms.TextInput(attrs = {'size': 15}))
You could be having problems with how the field is defined in your database.
This seems like it could be the common situation of doing syncdb with the field initially not having blank=True null=True and then adding those terms later (after initial syncdb) to make the field not required. These settings will not be applied by simply running syncdb again. It requires a database migration or a database flush (will clear existing data, which isn't necessarily bad in development).

Categories