built-in send_email in a form to link a field to another model via foreign key, Data not coming up

I am trying to use built-in send_email in a Ticket form. Everything is working fine, but when I link a field to another model via foreign key, Data doesnt get pulled. Please kindly help me in syntax.

views.py

def createTicket(request):
form = TicketForm(request.POST)
if request.method == 'POST':
    if form.is_valid():
        subject = "Ticket Email"
        body = {
            'customer': form.cleaned_data.get('customer'),
            'subject': form.cleaned_data.get('subject'),
            'priority': form.cleaned_data.get('priority'),
            'details': form.cleaned_data.get('details'),
        }
        message = "\n".join(body.values())
        form.save()
        try:
            send_mail(subject, message, 'from_email', [form.cleaned_data.get('technician_email')])
        except BadHeaderError:
            return HttpResponse('Invalid Header')
        return redirect('/')
context = {'form': form}
return render(request, 'ticket_form.html', context)

models.py

class Ticket(models.Model):
PRIORITY = (
    ('normal', 'Normal'),
    ('urgent', 'Urgent')
)
STATUS = (
    ('pending', 'Pending'),
    ('hold', 'Hold'),
    ('closed', 'Closed')
)
customer = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
technician = models.CharField(max_length=255, null=True)
technician_email = models.CharField(max_length=255, null=True)
subject = models.CharField(max_length=255, blank=True, null=True)
priority = models.CharField(max_length=255, blank=True, null=True, choices=PRIORITY)
details = models.CharField(max_length=2000, blank=True, null=True)

class Customer(models.Model):
company_name = models.CharField(max_length=255, null=True)
Customer_type = models.ManyToManyField(Customertype, blank=True)
first_name = models.CharField(max_length=255, null=True)
last_name = models.CharField(blank=True, max_length=255, null=True)

TRACEBACK ERROR:

Environment:
Request Method: POST
Request URL: http://localhost:8000/create_ticket/

Django Version: 3.2.4
Python Version: 3.7.9
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'accounts.apps.AccountsConfig',
 'django_filters',
 'widget_tweaks',
 'datetimewidget',
 'crispy_forms']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.locale.LocaleMiddleware']



Traceback (most recent call last):
  File "D:\development\website\venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "D:\development\website\venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\development\website\venv\lib\site-packages\django\contrib\auth\decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "D:\development\demo\accounts\decorators.py", line 21, in wrapper_function
    return view_function(request, *args, **kwargs)
  File "D:\development\demo\accounts\views.py", line 259, in createTicket
    message = "\n".join(body.values())

Exception Type: TypeError at /create_ticket/
Exception Value: sequence item 0: expected str instance, NoneType found

Is this the right way for below code to put in the message body ?

'customer': form.cleaned_data.get('customer'),

Please kindly suggest, Thanks.

We’re probably going to need to see your TicketForm class.

Note that your customer field in Ticket has null=True. This means that there’s no requirement for the form being submitted to contain a reference to a customer.

Thanks for helping me out,
Here is the TicketForm Class:

class TicketForm(ModelForm):
    class Meta:
        model = Ticket
        fields = '__all__'
        widgets = {
        'details': Textarea,
         }

I need customer name from the drop down to be sent in email, which is not happening. Please help.

Couple different things:

This isn’t going to work unless all the values in body are populated with strings. This is what’s causing your root error.

Because of the above, this:

really isn’t your best way of trying to handle this.

You’re already saving the form at:

This function returns the instance of the model being saved from the form. You can retrieve the values needed from that saved instance.

Thanks for replying. I am new to Django and I changed my code to below:

customer = request.POST.get('customer')
send_mail(subject, customer, 'from@email.com', 'to@email.com')

Now I am getting ID value instead of customer name, what am I missing ?

I’m guessing (because there’s not enough information presented here to know) that you’re getting the PK of the customer. You would need to query the appropriate model to get the fields from that model.

For a more specific response, please repost the complete view in its current form.

Sure, below are the code. I also slightly modified models.py to below for class Customer. I changed from ‘first_name’ to ‘name’.

models.py

class Customer(models.Model):
    name = models.CharField(max_length=255, null=True)
    last_name = models.CharField(blank=True, max_length=255, null=True)
    company_name = models.CharField(max_length=255, null=True)
    Customer_type = models.ManyToManyField(Customertype, blank=True)
   
    def __str__(self):
        return self.name

class Ticket(models.Model):
    PRIORITY = (
        ('normal', 'Normal'),
        ('urgent', 'Urgent')
    )
    STATUS = (
        ('pending', 'Pending'),
        ('hold', 'Hold'),
        ('closed', 'Closed')
    )
    customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True)
    technician = models.CharField(max_length=255, null=True)
    technician_email = models.CharField(max_length=255, null=True)
    subject = models.CharField(max_length=255, blank=True, null=True)
    priority = models.CharField(max_length=255, blank=True, null=True, choices=PRIORITY)
    ticket_status = models.CharField(max_length=255, blank=True, null=True, choices=STATUS)
    details = models.CharField(max_length=2000, blank=True, null=True)

views.py

def createTicket(request):
    form = TicketForm(request.POST)
    if request.method == 'POST':
        if form.is_valid():
            subject = "New Ticket"
            body = {
                'customer': request.POST.get('customer'),
                'subject': form.cleaned_data.get('subject'),
                'priority': form.cleaned_data.get('priority'),
                'details': form.cleaned_data.get('details'),
            }
            form.save()
            message = "\n".join(body.values())
            try:
                send_mail(subject, message, 'test123@domain.com', [form.cleaned_data.get('technician_email')])
            except BadHeaderError:
                return HttpResponse('Invalid Header')
            return redirect('/')
    context = {'form': form}
    return render(request, 'ticket_form.html', context)

def customer(request, pk_test):
    customer = Customer.objects.get(id=pk_test)
    ticket = Ticket.objects.all()
    context = {'customer': customer,
               'ticket': ticket}
    return render(request, 'customer.html', context)

def viewTicket(request):
    ticket = Ticket.objects.all()
    ticketFilter = TicketFilter(request.GET, queryset=ticket)
    ticket = ticketFilter.qs 
    context = {'ticket': ticket,
               'ticketFilter': ticketFilter}
    return render(request, 'view_ticket.html', context)

The Email output:

8
New Ticket
urgent
This is Test

I am getting customer ID ‘8’ instead of his name in the email. How to fix it ?

Stop working with the POST data directly.

You’re binding that data to a form, and saving the form. The process of saving the form gives you the instance of the model being saved.

Use that instance of the model to retrieve any attributes you may need in the remainder of your view.