Many toys have same information(for example description and price) how can i customise my admin.py in order to get last saved object values, everytime when i press add new toy in admin panel and display them in my input fields.
models.py
class Toy(models.Model):
name = models.CharField(max_length=255)
description = TextField()
quantity = models.FloatField(default=0.0)
price = models.FloatField()
def __unicode__(self):
return self.name
admin.py
admin.site.register(Toy)
Try to do it like that:
First create a form in your admin.py:
class ToyForm(forms.ModelForm):
class Meta:
model = Toy
fields = ('__all__')
def __init__(self, *args, **kwargs):
#if not an edit
if 'instance' not in kwargs:
#we get the last object
last_object = Toy.objects.all().order_by("id")[0]
#we add the last object informations to the initial data
initial = {'description': last_object.description, 'price': last_object.price}
kwargs['initial'] = initial
super(ToyForm, self).__init__(*args, **kwargs)
Then add this form to the Admin model:
class ToyAdmin(admin.ModelAdmin):
form = ToyForm
Fianally:
admin.site.register(ToyAdmin, Toy)
In modeladmin class there is a method get_form, you can override the method to return, the values returned from you conditioned queryset results ( Mymodel.objects.last() )
from django.contrib import admin
#admin.register(Toy)
class ToyAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(ToyAdmin, self).get_form(request, obj, **kwargs)
# code logic to check condition & queryset like Toy.objects.last()
form.base_fields['name'].initial = 'value'
return form
Related
this is my views.py :
i want save type in device field in model
class GetDeviceMixin( object):
def setup(self, request, *args, **kwargs):
super().setup( request, *args, **kwargs)
type= request.META['HTTP_USER_AGENT']
print(type)
return type
class RegisterView(GetDeviceMixin , generic.CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy("register")
template_name = "man/man.html"
and this is my models.py
class account(AbstractBaseUser):
first_name= models.CharField(max_length=20,verbose_name="first name")
device = models.CharField(verbose_name="device" , max_length=100)
this is my forms.py:
class GetReq(forms.ModelForm):
class Meta:
model = account
fields = ['device',]
First, pop Classy CBVs onto your browser bookmark list ...
I'm assuming that you want to use a form, either to get other information from the user, or to allow the user to override the automatically determined value of device. In this case, you want to pass it as the initial value for device to the form
Now, look at CreateView to work out what to subclass. get_initial() looks hopeful, so
def get_initial(self):
initial = super().get_initial()
initial['device'] = self.device_type # as per the comment!
return initial
You should now see a form with the automatically determined value as the default value
If the intent was to get other fields of the model from the user and to always forcibly insert the automatically determined device_type, you would instead subclass form_valid
def form_valid(form):
obj = form.save( commit=False)
obj.device = self.device_type
obj.save()
So I've a formset tied to a model and one of the fields in that is ForeignKey.
models.py
class Squad(models.Model):
rid = models.AutoField(primary_key=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
def __str__(self):
return self.team.tname
forms.py
class SquadForm(ModelForm):
class Meta:
model = Squad
def __init__(self, logged_user, user, *args, **kwargs):
super(SquadForm, self).__init__(*args, **kwargs)
self.fields['team'] = forms.ModelChoiceField(queryset=Team.rows.get_my_teams(user=logged_user), empty_label="None")
As you can see, the __init__ function is expecting an extra parameter logged_user which I'm hoping to pass via the views.py file. But if I do the following:
views.py
def choose_teams(request):
teamformset = modelformset_factory(Squad, extra=2, form=SquadForm(request.user))
form = teamformset(queryset=Squad.objects.none())
return render(request, 'foo.html', {'form':form})
I'm trying to pass the logged in user as a parameter on line 2 but this is resulting in the following message:
Field 'id' expected a number but got 'SquadForm'
Not sure what I'm missing here. But if I remove the parameter from line 2:
teamformset = modelformset_factory(Squad, extra=series.team_number, form=SquadForm)
it starts working (of course, I no longer expect the user in the forms.py file and remove it too) but shows all the data and not filtered one.
You can pass additional keyword arguments to your formset form by passing form_kwargs={} to your formset
class SquadForm(ModelForm):
class Meta:
model = Squad
def __init__(self, *args, logged_user, **kwargs):
super(SquadForm, self).__init__(*args, **kwargs)
self.fields['team'] = forms.ModelChoiceField(queryset=Team.rows.get_my_teams(user=logged_user), empty_label="None")
teamformset = modelformset_factory(Squad, extra=2, form=SquadForm)
form = teamformset(queryset=Squad.objects.none(), form_kwargs={'logged_user': request.user})
I'm using Django 2.1 and PostgreSQL.
My problem is that I'm trying to create a form to edit two different models at the same time. This models are related with a FK, and every example that I see is with the user and profile models, but with that I can't replicate what I really need.
My models simplified to show the related information about them are:
# base model for Campaigns.
class CampaignBase(models.Model):
....
project = models.ForeignKey(Project, on_delete=models.CASCADE)
creation_date = models.DateTimeField(auto_now_add=True)
start_date = models.DateTimeField(null=True, blank=True)
end_date = models.DateTimeField(null=True, blank=True)
....
# define investment campaign made on a project.
class InvestmentCampaign(models.Model):
....
campaign = models.ForeignKey(CampaignBase, on_delete=models.CASCADE, null=True, blank=True)
description = models.CharField(
blank=True,
max_length=25000,
)
....
And the form that I want to create is one that includes the end_date of the FK CampaignBase, and the Description from the InvestmentCampaign.
Now I have this UpdateView to edit the InvestmentCampaign, and I need to adapt to my actual needs, that are also update the CampaignBase model:
class ProjectEditInvestmentCampaignView(LoginRequiredMixin, SuccessMessageMixin, generic.UpdateView):
template_name = 'webplatform/project_edit_investment_campaign.html'
model = InvestmentCampaign
form_class = CreateInvestmentCampaignForm
success_message = 'Investment campaign updated!'
def get_success_url(self):
return reverse_lazy('project-update-investment-campaign', args=(self.kwargs['project'], self.kwargs['pk']))
# Make the view only available for the users with current fields
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
# here you can make your custom validation for any particular user
if request.user != self.object.campaign.project.user:
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
# Set field as current user
def form_valid(self, form):
campaign = InvestmentCampaign.objects.get(pk=self.kwargs['campaign'])
form.instance.campaign = campaign
form.instance.history_change_reason = 'Investment campaign updated'
return super(ProjectEditInvestmentCampaignView, self).form_valid(form)
def get_context_data(self, **kwargs):
project = Project.objects.get(pk=self.kwargs['project'])
context = super(ProjectEditInvestmentCampaignView, self).get_context_data(**kwargs)
context['project'] = project
return context
My forms are:
class CreateCampaignBaseForm(forms.ModelForm):
class Meta:
model = CampaignBase
fields = ('end_date',)
widgets = {
'end_date': DateTimePickerInput(),
}
def __init__(self, *args, **kwargs):
# first call parent's constructor
super(CreateCampaignBaseForm, self).__init__(*args, **kwargs)
# evade all labels and help text to appear when using "as_crispy_tag"
self.helper = FormHelper(self)
self.helper.form_show_labels = False
self.helper._help_text_inline = True
class CreateInvestmentCampaignForm(forms.ModelForm):
class Meta:
model = InvestmentCampaign
fields = ('description')
widgets = {
'description': SummernoteWidget(attrs={'summernote': {
'placeholder': 'Add some details of the Investment Campaign here...'}}),
}
def __init__(self, *args, **kwargs):
# first call parent's constructor
super(CreateInvestmentCampaignForm, self).__init__(*args, **kwargs)
# evade all labels and help text to appear when using "as_crispy_tag"
self.helper = FormHelper(self)
self.helper.form_show_labels = False
self.helper._help_text_inline = True
I've read everywhere that the best way of doing this is using function based views, and call each of the forms that I have and then do the validation. the thing is that I don't know how can I populate the fields with the right object in both forms, and also, I don't know how to do the equivalent of the get_context_data nor getting the self arguments to do the equivalent of the get_success_url (because with function based views I only have the request attr so I can't access the kwargs).
I've seen some people using the django-betterforms, but again, the only examples are with the auth and profile models and I don't see the way to replicate that with my own models.
Thank you very much.
If the only thing you want to change is one field end_date on BaseCampaign, then you should use just one form. Just add end_date as an additional field (e.g. forms.DateTimeField()) on your CreateInvestmentCampaignForm and in your form.valid() method, after saving the form, set the value on the associated campaign:
def form_valid(self, form):
inv_campaign = form.save(commit=False)
inv_campaign.campaign.end_date = form.cleaned_data['end_date']
inv_campaign.campaign.save()
inv_campaign.history_change_reason = ...
return super().form_valid(form)
Here's how to add end_date to your form and initialize it correctly:
class CreateInvestmentCampaignForm(ModelForm):
end_date = forms.DateTimeField(blank=True)
class Meta:
model = InvestmentCampaign
fields = ('description')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.campaign:
self.fields['end_date'].initial = self.instance.campaign.end_date
Based on the conversation on the answer of #dirkgroten, I've developed what worked for me and what I'm actually using, but I market his answer as correct because his code is also functional.
So, meanwhile he is initiating the values on the form, I'm using the view to do that by adding a def get_initial(self): and also adding the validation on the def form_valid(self, form)::
On the view:
...
def get_initial(self):
"""
Returns the initial data to use for forms on this view.
"""
initial = super(ProjectEditInvestmentCampaignView, self).get_initial()
initial['end_date'] = self.object.campaign.end_date
return initial
...
# Set field as current user
def form_valid(self, form):
form.instance.history_change_reason = 'Investment campaign updated'
is_valid = super(ProjectEditInvestmentCampaignView, self).form_valid(form)
if is_valid:
# the base campaign fields
campaign = form.instance.campaign
campaign.end_date = form.cleaned_data.get("end_date")
campaign.save()
return is_valid
And on the form I just added the end_date field:
class CreateInvestmentCampaignForm(forms.ModelForm):
end_date = forms.DateTimeField()
class Meta:
model = InvestmentCampaign
fields = ('description',)
widgets = {
'description': SummernoteWidget(attrs={'summernote': {
'placeholder': 'Add some details of the Investment Campaign here...'}}),
'end_date': DateTimePickerInput(), # format='%d/%m/%Y %H:%M')
}
def __init__(self, *args, **kwargs):
# first call parent's constructor
super(CreateInvestmentCampaignForm, self).__init__(*args, **kwargs)
# evade all labels and help text to appear when using "as_crispy_tag"
self.helper = FormHelper(self)
self.helper.form_show_labels = False
self.helper._help_text_inline = True
I want to set the item in dropdown using the query in the form. I want to add employee and the select company which using filter Is_Del= 0. I do not know how to set values for the drop down and where to write this query.
I tried to put in Forms.py, but it is not working.
This is form.py
class EmployeeCreateForm(forms.ModelForm):
class Meta:
model = Employee
fields = ('Emp_Name','Emp_company','Emp_Dept','Emp_Join_Date', 'Emp_End_Date')
def clean(self):
cleaned_data = super(EmployeeCreateForm, self).clean()
Emp_Name = cleaned_data.get('Emp_Name')
Emp_company = cleaned_data.get('Emp_company')
Emp_Dept = cleaned_data.get('Emp_Dept')
Emp_Join_Date = cleaned_data.get('Emp_Join_Date')
Emp_End_Date = cleaned_data.get('Emp_End_Date')
return cleaned_data
def __init__(self, *args, **kwargs):
super(EmployeeCreateForm,self).__init__(*args, **kwargs)
self.fields['Emp_company'].queryset = Company.objects.filter(Is_Del=0)
and below is my view.py
class EmployeeCraeteView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
model=Employee
form = EmployeeCreateForm
success_message = " Employee Craeted successfully!"
success_url="../../company/all-companies"
template_name = 'employee_form.html'
fields =[
'Emp_Name','Emp_company','Emp_Dept','Emp_Join_Date',
'Emp_End_Date'
]
companies= Company.objects.filter(Is_Del=0)
def form_valid(self,form):
form.instance.Emp_Crt_By = self.request.user
if form.cleaned_data['Emp_Join_Date'] >= form.cleaned_data['Emp_End_Date']:
form.add_error('Emp_End_Date', 'Joining date should be less than Ending date')
return self.form_invalid(form)
return super(EmployeeCraeteView, self).form_valid(form)
I want to show only this companies in the form which are filtered by Is_Del =0
Your EmployeeCreateView is wrong:
Remove the attributes form, fields and companies
Add form_class = EmployeeCreateForm.
The reason is that form doesn't do anything in a CreateView (see here). To use a custom form class, you need to pass it to form_class.
Your CreateView was dynamically creating the form using a modelform_factory with the fields you defined (if you hadn't added those you'd have seen your mistake immediately) and so your EmployeeCreateForm is never instantiated.
admin.py
class PromoAdmin(admin.modelAdmin)
list_display = ( 'name', 'id', 'category', 'promo_type', 'store', 'brand', 'date_start' )
form = SampleForm
forms.py
class SampleForm(forms.ModelForm):
class Meta:
model = Promo
def __init__(self, request *args, **kwargs):
super(PromoAdminForm, self).__init__(*args, **kwargs)
self.fields["store"].queryset = Store.objects.filter(onwer=request.user)
got an error on request
Django Version: 1.3.1
Exception Type: TypeError
Exception Value:
init() takes at least 2 arguments (1 given)
You cannot initiate the store field with request.user in the field declaration. You can try the following:
class MyAwesomeForm(forms.ModelForm):
store = forms.ModelChoiceField(Store.objects)
class Meta:
model = Promo
def __init__(self, user, *args, **kwargs):
super(MyAwesomeForm, self).__init__(*args, **kwargs)
self.fields['store'].queryset = Store.objects.filter(owner=user)
While instantiating the form you can pass the request.user object.
myform = MyAwesomeForm(request.user)
If you want to achieve this in the admin you might try this
For providing only the objects related to the logged-in user in the admin provides the possibility to overwrite ModelAdmin.queryset function:
class MyModelAdmin(admin.ModelAdmin):
form = MyAwesomeAdminForm()
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(store__owner=request.user)
class MyAwesomeAdminForm(forms.ModelForm):
class Meta:
model = Promo
Note that store__owner only works if you have a foreign key field stored in your promo model as such:
class Promo(models.Model):
store = models.ForeignKey(Store)
class Store(models.Model):
owner = models.ForeignKey(User)
I assume it should also be possible to somehow pass the request to the init method of the form. But did not find a suitable approach to do it.