In our model we have a name and slug fields. This is how it looks like:
class MyModel(CommonInfo, SafeDeleteModel):
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True,)
For the slug field we generate an unique slug every time our model is saved.
We are also using Django Safe Delete library to soft delete our model:
Django Safe Delete
Django Autoslug
That means that for example if we create a new instance of our model with Name "My Model" it will auto generate a slug that will look like this: "/my-model".
Now let's say we "soft delete" this instance with the slug "/my-model". In our database there will be a property deleted which contains the date when the model was deleted. We don't show this one in our application, it is completely ignored (because it is soft deleted, that's fine).
The problem is that next time we create another one with the same name "My Model" it will auto generate the slug "/my-model" again, not considering that there is already one (which is soft deleted) with the same name and slug. We would need something like "/my-model-1" or whatever that is unique.
We are missing the connection between the autoslug and the safe-delete libraries, somehow the autoslug needs to know that there might be soft deleted ones and consider them when generating the unique slug.
Any help would be really appreciated and please consider that we are totally new in Django / Python.
if this doesn't work, our workaround will be generating the slug using 2 fields (name & id). It will generate a slug that will look like this: "/my-model/12345" and will be always unique since id is unique.
I think we found it.
We needed to create a new Manager that can see all the instances, even the soft deleted ones:
class MyModelAllManager(SafeDeleteManager):
_safedelete_visibility = DELETED_VISIBLE
Then in our model we pass it to the AutoSlugField function:
class MyModel(CommonInfo, SafeDeleteModel):
# We don't know why but it only works if we create a variable and assign the Manager to it. It doesn't work if we pass the Manager directly to the AutoSlugField function.
all_objects = MyModelAllManager()
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True, manager=all_objects)
That does the magic.
Related
I have two models: Setting and SettingsGroup.
When someone clicks on a specific SettingsGroup in the Django Admin and the edit/detail page appears I'd like for the child Setting objects to be displayed but as a list not a form.
I know that Django has InlineModelAdmin but this displays the children as editable forms.
My concern isn't with the child objects being editable from the parent object but rather the amount of space it consumes. I'd rather have a list with either a link to the appropriate child record or that changes a particular object to be inline editable.
Here is my Setting model:
class Setting(models.Model):
key = models.CharField(max_length=255, blank=False)
value = models.TextField(blank=True)
group = models.ForeignKey('SettingsGroup', blank=True,
on_delete=models.SET_NULL, null=True)
def __str__(self):
return str(self.key)
And the SettingsGroup model:
class SettingsGroup(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
def __str__(self):
return str(self.name)
The method I don't want to use (or need to find a different way to use) is InlineModelAdmin which appears in my admin.py currently as:
class SettingsGroupInline(admin.StackedInlin):
model = Setting
fk_name = 'group'
#admin.register(SettingsGroup)
class SettingsGroupAdmin(admin.ModelAdmin):
inlines = [ SettingGroupsInline, ]
Here is an example of how I'd like it to work:
There is a MySettings object, an instance of the SettingsGroup model.
There is a CoolSetting object and a BoringSetting object, each an instance of the Setting model.
The CoolSetting object has its group set to the MySettings object.
The BoringSetting object does not have a group set.
When I open the detail/edit view of the Django Admin for the MySettings object I see the normal edit form for the MySettings object and below it the CoolSetting object (but not as a form).
I do not see the BoringSetting object because it is not a child/member/related of/to MySettings.
I have some ideas on how this could be accomplished but this seems like fairly basic functionality and I don't want to go building something if Django (or other existing code) provides a way to accomplish this.
Any ideas?
Why can't you just access the children using something like Setting.objects.filter(group=SettingsGroup.objects.get(name={name}))
If being presented in a template you could pass the SettingsGroup name to the context and iterate over the children and present them however you like.
I may not understand your question exactly so if this is not what you're looking for let me know!
I am trying to link venues to the products they supply. The products supplied are not unique to each venue.
As a result, Venue 1 and 2 could both provide Product A.
The outcome I am looking for is twofold:
when a Product is added to the database, there is an option to link it to an existing Venue
When looking at a venue in particular, I would like to have the list of all the product that can be supplied
Outcome 1. and current problem
I tried using Foreign Keys and ManyToManyFields but this only seems to add all the products available to the database to all the venues without leaving a choice.
This is what venue = models.ManyToManyField(Venue, blank=True, related_name="available_products") renders in the admin panel. In this example, by adding ManyToMany Field all Venues have been added to Product 1. Whereas I would like the possibility to add only specific venues (not all)
Outcome 2. and current problem
The second problem is obviously referring to Product from the Venue model. If I input a foreign key or any form of relation in it, Django gets upset and tells me Product is not defined.
I thought of creating a 3rd model, that could combine both Venue and Products, but it feels like there must be something more sophisticated that could done.
(edit: I replaced the FK by ManyToManyField as suggested by David Schultz)
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, null=True, blank=True)
class Product(models.Model):
name = models.CharField('Product Name', max_length=120, null=True)
venue = models.ManyToManyField(Venue, blank=True, related_name="available_products")
A ManyToManyField should in fact be perfect for what you want to do. It only associates those objects to one another for which relations have been explicitly created, e.g. in the admin or programmatically. The fact that your admin shows you all objects at once does not mean that they have been assigned, but just that they are available. In the list from your screenshot, selection can be done by Ctrl+Mouseklick, and when you then save the Product and reload the page, precisely the Venues you selected before should now again show up with a distinct background color – this means that they have indeed been saved.
Regarding your second problem: The argument related_name works differently than you apparently think: In your last line of code, you should rather write something like related_name="available_products", because related_name becomes the name of an attribute of your Venue instances, by which you can then access all Product objects that have been associated to that Venue object, e.g. like so: venue.available_products.all()
related_name works the same for ManyToManyField and ForeignKey.
You can define your ManyToManyField either on Product or on Venue; some more info is in the documentation page. So all in all, you should do something like:
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, blank=True)
class Product(models.Model):
name = models.CharField('Product Name', max_length=120, blank=True)
venues = models.ManyToManyField(Venue, blank=True, related_name="available_products")
(Side note: For CharFields, it is recommended not to set null=True and instead only use blank=True, because otherwise there would be two different options for "no data", namely Null and an empy string. Details in the docs.)
This is my model.py code
class Question(models.Model):
id = models.AutoField
question = models.CharField(max_length=100)
answer = models.CharField(max_length=100)
class TestSeries(models.Model):
id = models.AutoField
quiz_name = models.CharField(max_length=100)
all_question=models.ManyToManyField(MyQuestion)
When i open my admin panel on test series
previous added are shown in order : Oldest first
I want to see that in newest first manner.
There are two things OP needs to do
Store in the models when the object was created / updated.
Order based on the creation date. To do so, one can add to the model a Meta option named ordering or define a custom form to be used in the Django Admin. Example 1, Example 2.
For example, I have two models in a car management system(webpage):
class Brand(models.Model):
brand_name= models.CharField(max_length=100, null=False)
class Cars(models.Model):
car_model= models.CharField(max_length=100, null=False)
car_production_year= models.CharField(max_length=100, null=False)
car_brand= models.ForeignKey(Brand, null=True, blank=True, default = None)
Now, I want to delete a Brand data from the car system. How can I check if this Brand has been used in another model or does that foreign key contains any data(as I have allowed null True for car_brand in Cars model).
PS:
using this function:
self.model._meta.get_all_related_objects():
I got any related object in used in Brand model class. But, I don't know how to get if that related object contains any data.
Use exists(). It's designed to be used for exactly this type of situations:
for brand in Brand.objects.all():
if not brand.cars_set.all().exists():
# delete
Also it's almost always faster than any other type of check because of the way it is designed to work at the database level. You can read details of exists() behaviour in the docs
From this question, for my code, this was faster:
for b in Brand.objects.filter(cars__isnull=True):
# delete
If You have the ids (my case), I used this (60% faster than cars_set):
for brand_id in brand_id_set:
if Brand.objects.filter(id=brand_id).filter(cars__isnull=True):
# delete
I am using Django 1.11
I'd say the easiest way is like so:
# brand is an instance of Brand
if not brand.cars_set.all():
# delete
else:
# do something else
The Django docs cover foreign keys in quite a lot of detail.
cars_with_brands = Car.objects.filter(car_brand__isnull = False)
if cars_with_brands.count() == 0:
# you can delete Brand Model
else:
cars_with_brands.delete()
# you can delete Brand Model
I have the two models shown below:
class EntryImage(models.Model):
image = models.ImageField(upload_to="entries")
class Entry(models.Model):
code = models.CharField(max_length=70, unique=True)
images = models.ManyToManyField(EntryImage, null=True, blank=True)
As you can see, Entry can have 0 or more images.
My question is: Is it possible to have that kind of
schema and dynamically change the upload_to based on the Entry code?
Well without going too far off, you could make an intermediary M2M table EntryImageDir with the directory name in it. You would link your EntryImages there with a foreign key and you could create the EntryImageDir either with a signal on Entry create or when uploading something.
The documentation for M2M with custom fields is here:
http://www.djangoproject.com/documentation/models/m2m_intermediary/
You can make upload_to a callable, in which case it will be called and passed the instance of the model it is on. However, this may not have been saved yet, in which case you may not be able to query the Entry code.