Asked  7 Months ago    Answers:  5   Viewed   147 times

Say I have the following in my models.py:

class Company(models.Model):
   name = ...

class Rate(models.Model):
   company = models.ForeignKey(Company)
   name = ...

class Client(models.Model):
   name = ...
   company = models.ForeignKey(Company)
   base_rate = models.ForeignKey(Rate)

I.e. there are multiple Companies, each having a range of Rates and Clients. Each Client should have a base Rate that is chosen from it's parent Company's Rates, not another Company's Rates.

When creating a form for adding a Client, I would like to remove the Company choices (as that has already been selected via an "Add Client" button on the Company page) and limit the Rate choices to that Company as well.

How do I go about this in Django 1.0?

My current forms.py file is just boilerplate at the moment:

from models import *
from django.forms import ModelForm

class ClientForm(ModelForm):
    class Meta:
        model = Client

And the views.py is also basic:

from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *

def addclient(request, company_id):
    the_company = get_object_or_404(Company, id=company_id)

    if request.POST:
        form = ClientForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(the_company.get_clients_url())
    else:
        form = ClientForm()

    return render_to_response('addclient.html', {'form': form, 'the_company':the_company})

In Django 0.96 I was able to hack this in by doing something like the following before rendering the template:

manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]

ForeignKey.limit_choices_to seems promising but I don't know how to pass in the_company.id and I'm not clear if that will work outside the Admin interface anyway.

Thanks. (This seems like a pretty basic request but if I should redesign something I'm open to suggestions.)

 Answers

43

ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet. See the reference for ModelChoiceField.

So, provide a QuerySet to the field's queryset attribute. Depends on how your form is built. If you build an explicit form, you'll have fields named directly.

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

If you take the default ModelForm object, form.fields["rate"].queryset = ...

This is done explicitly in the view. No hacking around.

Tuesday, June 1, 2021
 
Chvanikoff
answered 7 Months ago
54

One way:

The best way I found to do this is using RunSQL:

Migrations contains the RunSQL class. To do this:

  1. ./manage.py makemigrations --empty myApp
  2. edit the created migrations file to include:

operations = [ migrations.RunSQL('RAW SQL CODE') ]

As Nathaniel Knight mentioned, RunSQL also accepts a reverse_sql parameter for reversing the migration. See the docs for details

Another way

The way I solved my problem initially was using the post_migrate signal to call a cursor to execute my raw SQL.

What I had to add to my app was this:

in the __init__.py of myApp add:

default_app_config = 'myApp.apps.MyAppConfig'

Create a file apps.py:

from django.apps import AppConfig
from django.db.models.signals import post_migrate
from myApp.db_partition_triggers import create_partition_triggers


class MyAppConfig(AppConfig):
    name = 'myApp'
    verbose_name = "My App"

    def ready(self):
        post_migrate.connect(create_partition_triggers, sender=self)

New file db_partition_triggers.py:

from django.db import connection


def create_partition_triggers(**kwargs):
    print '  (re)creating partition triggers for myApp...'
    trigger_sql = "CREATE OR REPLACE FUNCTION...; IF NOT EXISTS(...) CREATE TRIGGER..."
    cursor = connection.cursor()
    cursor.execute(trigger_sql)
    print '  Done creating partition triggers.'

Now on every manage.py syncdb or manage.py migrate this function is called. So make sure it uses CREATE OR REPLACE and IF NOT EXISTS. So it can handle existing functions.

Wednesday, August 11, 2021
 
Thermatix
answered 4 Months ago
98

You can do that by inheriting from the ChooserBlock.

class SomeSortOfIconChooserBlock(blocks.ChooserBlock):
    target_model = SomeSortOfIcon
    widget = forms.Select

    class Meta:
        icon = "icon"

    # Return the key value for the select field
    def value_for_form(self, value):
        if isinstance(value, self.target_model):
            return value.pk
        else:
            return value

and in your block just use

class SomeBox(blocks.StructBlock):
    headline = blocks.TextBlock(required=True)
    some_icon = SomeSortOfIconChooserBlock(required=True)
    info_box_content = blocks.RichTextBlock(label='Content', required=True)

    class Meta:
        template = 'blocks/some_box.html'
        icon = 'form'
        label = _('Some Box')

This will give you a drop down based on the objects of the SomeSortOfIcon model.

Thursday, October 14, 2021
 
somedev
answered 2 Months ago
75

the issue was actually that this line

<form method="post" id="form" action="" enctype="multipart/form-data" class="infotabs accfrm">

appeared as

<form method="post" id="form" action="" enctype="multipart/form
-data" class="infotabs accfrm">

hence the forms were not uploading. And to think I have had this issue for 2 weeks. If its ok with you guys, i will delete this question.

Sunday, October 24, 2021
 
user123
answered 2 Months ago
79

This is how you do it to a non django-crispy-forms form:

from django import forms

from . import models


class DriftwoodForm(forms.ModelForm):
    class Meta:
        model = models.Driftwood
        fields = ('user', 'title', 'description', 'image')

    image = forms.ImageField()

full documentation: https://docs.djangoproject.com/en/1.8/ref/forms/fields/#django.forms.ImageField


Now what you have to do is simply use the form as you always do, calling save() on the form won't try to brute save the image to the model specified in the Meta class, but you will be able to do whatever you want with the field.

I'm not sure thou, but i suppose you can do the same with django-crispy-forms, just add the field below and suppose its from the model itself.

Sunday, November 7, 2021
 
Narendra Singh
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share