I've developed my personal blog to learn Django, I would like to make a backup of every post on a second model. Below there is an example of my aim:
from django.db import models
class mainModel(models.Model):
text = models.CharField(max_length=250)
class copyModel(models.Model):
main = models.ForeignKey(mainModel, on_delete=models.CASCADE)
text = models.CharField(max_length=250)
def save(self, *args, **kwargs):
self.text = mainModel.text
super(copyModel, self).save(*args, **kwargs)
When I create or modify a post into mainModel must be create a copy of it into copyModel. With the code above I can create a "post" but the save method from copyModel doesen't work.
Is this the right way?
You have to override mainModel save method instead of copyModel
from django.db import models
class mainModel(models.Model):
text = models.CharField(max_length=250)
def save(self, *args, **kwargs):
super(mainModel, self).save(*args, **kwargs)
copyModel.objects.create(
main=self, text=self.text)
class copyModel(models.Model):
main = models.ForeignKey(mainModel, on_delete=models.CASCADE)
text = models.CharField(max_length=250)
Related
I have Django Model that has a live boolean attribute.
I want the View to get the Model by slug and only go to this page if it's live USING THE DetailView (not function based view, because I want to see how to do it.
Model definition
# myapp/models.py
class MyModel(models.Model):
name = models.CharField(max_length=255)
live = models.BooleanField(default=True)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
I hoped it would be done something like this:
class ModelDetailView(DetailView):
model = MyModel
def get(self, request, *args, **kwargs):
service = self.get_object_or_404(Service, live=True) # <- Main point of what I'm looking for help with
return super().get(request, *args, *kwargs)
Is there a way to filter this way?
You can specify the queryset to filter, so:
class ModelDetailView(DetailView):
model = MyModel
queryset = MyModel.objects.filter(live=True)
You thus do not need to implement the .get(…) method at all.
I have a Category model. I want to make a directory for the category everytime I create a new category. I have a method in my model called create_directory.
class Category(models.Model):
category_title = models.CharField(max_length=200)
category_image = models.ImageField(upload_to="category")
category_description = models.TextField()
slug = models.SlugField(max_length=200, unique=True, default=1)
def create_directory(self):
gallery_path = os.path.abspath(
os.path.join(settings.MEDIA_ROOT, Category.slug))
if not os.path.isdir(gallery_path):
os.mkdir(gallery_path)
class Meta:
verbose_name_plural = "Categories"
unique_together = ("category_title", "slug")
def __str__(self):
return self.category_title
I want to call create_directory each time I create a category in the Admin panel.
First, I think you meant self.slug and not Category.slug. self.slug is the value of the slug field for that particular instance, while Category.slug is just an instance of the SlugField class.
You should override your model's save method to call create_directory:
class Category(models.Model):
...
def create_directory(self):
gallery_path = os.path.abspath(
os.path.join(settings.MEDIA_ROOT, self.slug))
if not os.path.isdir(gallery_path):
os.mkdir(gallery_path)
def save(self, *args, **kwargs):
if not self.pk:
self.create_directory()
super().save(*args, **kwargs)
Alternatively, you can create a signal for this:
models.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=Category)
def category_post_save(sender, instance, *args, **kwargs):
gallery_path = os.path.abspath(
os.path.join(settings.MEDIA_ROOT, instance.slug))
if not os.path.isdir(gallery_path):
os.mkdir(gallery_path)
Update
If you want to remove the directory as well, you can override the delete method:
class Category(models.Model):
...
def delete(self, *args, **kwargs):
os.rmdir(os.path.join(settings.MEDIA_ROOT, self.slug))
super().delete(*args, **kwargs)
There is also a pre_delete signal that you can use.
I want to call create_directory each time I create a category in the Admin panel.
As you want to call the create_directory method when a category is created from the admin panel, you should use save_model and not save.
You might want to read Difference between save() and save_model().
Simply you can do:
def save_model(self, request, obj, form, change):
self.create_directory()
super().save_model(request, obj, form, change)
I am wondering if it's possible to auto create a related model upon creation of the first model.
This is the models
class Team(models.Model):
name = models.CharField(max_length=55)
class TeamMember(models.Model):
team = models.ForeignKey('Team', on_delete=models.CASCADE, null=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=False)
So what I want to do is something like this on the 'Team' model
class Team(models.Model):
name = models.CharField(max_length=55)
#on_new.do_this
TeamMember.team = self
TeamMember.user = request.user
TeamMember.save()
I have tried to find any documentation about this. But only found some example about onetoonefields. But nothing about this.
Appreciate any help. Cheers!
I am assuming you are using forms to create team.
There is no direct way to create the TeamMember instance without the current user(via request). request is available in views only(unless you are using special middleware or third party library to access it), so we can send it form and create the user by overriding the save method of the modelform.
So you can try like this:
# Override the model form's save method to create related object
class TeamForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(TeamForm, self).__init__(*args, **kwargs)
class Meta:
model = Team
def save(self, **kwargs):
user = self.request.user
instance = super(TeamForm, self).save(**kwargs)
TeamUser.objects.create(team=instance, user=user)
return instance
And use this form in View:
# Update get_form_kwargs methods in create view
class TeamCreateView(CreateView):
form_class = TeamForm
template = 'your_template.html'
def get_form_kwargs(self):
kw = super(TeamCreateView, self).get_form_kwargs()
kw['request'] = self.request
return kw
Update
(from comments)If you have the user FK availble in Team then you can use it to create TeamMember by overriding the save method. Try like this:
class Team(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL)
name = models.CharField(max_length=55)
def save(self, *args, **kwargs): # <-- Override
instance = super(Team, self).save(*args, **kwargs)
TeamMember.objects.create(user=instance.user, team=instance)
return instance
How do I limit the values returned via the ManyToMany relationship and thus displayed in the <SELECT> field on my form to only show the spots which were created by the currently logged in user?
models.py
class Project(models.Model):
owner = models.ForeignKey(User, editable=False)
...
spots = models.ManyToManyField(to='Spot', blank=True, )
class Spot(models.Model):
owner = models.ForeignKey(User, editable=False)
spot_name = models.CharField(max_length=80, blank=False)
forms.py
from django import forms
from .models import Project, Spot
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
exclude = ('owner', )
class SpotForm(forms.ModelForm):
class Meta:
model = Spot
exclude = ('owner', )
I'm using GenericViews for Update and Create and currently see all of the entries everyone has made into Spots when I'm updating or creating a Project. I want to see only the entries entered by the logged in user. For completeness sake, yes, the project.owner and spot.owner were set to User when they were created.
I've tried def INIT in the forms.py and using limit_choices_to on the manytomany field in the model. Either I did those both wrong or that's not the right way to do it.
thank you!
in your forms.py
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
exclude = ('owner', )
def __init__(self, user_id, *args, **kwargs):
self.fields['spots'] = forms.ModelChoiceField(widget=forms.Select, queryset=Project.objects.filter(owner=user_id))
class SpotForm(forms.ModelForm):
class Meta:
model = Spot
exclude = ('owner', )
def __init__(self, user_id, *args, **kwargs):
self.fields['spot_name'] = forms.ModelChoiceField(widget=forms.Select, queryset=Spot.objects.filter(owner=user_id))
in your views.py
user_id = Project.objects.get(owner=request.user).owner
project_form = ProjectForm(user_id)
spot_form = SpotForm(user_id)
As I mentioned above, Dean's answer was really close, but didn't work for me. Primarily because request is not accessible in the view directly. Maybe it is in older Django versions? I'm on 1.9. Thank you Dean, you got me over the hump!
The gist of what's going on is adding User into the kwargs in the View, passing that to the ModelForm, remove User from the kwargs and use it to filter the Spots before the form is shown.
This is the code that worked for my project:
views.py
class ProjectUpdate(UpdateView):
model = Project
success_url = reverse_lazy('projects-mine')
form_class = ProjectForm
def dispatch(self, *args, **kwargs):
return super(ProjectUpdate, self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(ProjectUpdate, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
forms.py
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
exclude = ('owner', 'whispir_id')
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user')
super(ProjectForm, self).__init__(*args, **kwargs)
self.fields['spots'] = forms.ModelMultipleChoiceField(queryset=Spot.objects.filter(owner=user_id))
class SpotForm(forms.ModelForm):
class Meta:
model = Spot
exclude = ('owner', )
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user')
super(SpotForm, self).__init__(*args, **kwargs)
self.fields['spot_name'] = forms.ModelMultipleChoiceField(queryset=Spot.objects.filter(owner=user_id))
let's say I've the following very simple models:
class Customer(models.Model):
name = models.CharField(max_length=50)
class Probe(models.Model):
OwnerInfo = models.CharField(max_length=50)
comments = models.CharField(max_length=50)
customer = models.ForeignKey(Customer, null=True, blank=True)
I've been able to add an InLine to the Admin gui, but I'd like to use a SELECT component, so I can just select several Probes and assign them to the Customer. From this question:
one-to-many inline select with django admin
I know thanks to Luke's answer (last one) that I should create a custom Form and assign it to my ModelAdmin.form but I can not wonder how to tie it all together to make it work.
May anyone help?
Thanks a lot in advance.
OK, I came a step further, and now I've the field added to the Form, like this:
from django.contrib import admin
from django import forms
from web_gui.models import Probe, Customer, Firmware
class CustomerForm(forms.ModelForm):
probes = forms.ModelMultipleChoiceField(queryset=Probe.objects.all())
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['probes'].initial = [p.pk for p in Probe.objects.filter(customer_id=self.instance.pk)]
class Meta:
model = Customer
class CustomerAdmin(admin.ModelAdmin):
form = CustomerForm
admin.site.register(Probe)
admin.site.register(Customer, CustomerAdmin)
admin.site.register(Firmware)
but the initial values specified through "initial" are not being selected. What's wrong now? I assume that next will be to override the save() method to set the Probes on the Customer, am I right?
This is the best solution I've came up with. Let me know if there is any other better way of achieving this:
from django.contrib import admin
from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from web_gui.models import Probe, Customer, Firmware
class CustomerForm(forms.ModelForm):
probes = forms.ModelMultipleChoiceField(queryset = Probe.objects.all(), required=False)
probes.widget = FilteredSelectMultiple("Probes",False,attrs={'rows':'10'})
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['probes'].initial = [p.pk for p in Probe.objects.filter(customer_id=self.instance.pk)]
def save(self, force_insert=False, force_update=False, commit=True):
c = super(CustomerForm, self).save(commit=False)
c.probe_set = self.cleaned_data['probes']
c.save()
return c
class Meta:
model = Customer
class CustomerAdmin(admin.ModelAdmin):
form = CustomerForm
admin.site.register(Probe)
admin.site.register(Customer, CustomerAdmin)
admin.site.register(Firmware)