Django Framework - How to enable user to overwrite existing values if values already exists

Viewed 400

I am in a process to learn Django Framework and I want to understand how can I provide user with a pop up when data integrity is breached due to entry being placed for similar primary key. Since Django does not allows conjugate primary key I've used the unique_together functionality to perform a validity check on form. I've created the model as follows

Model.py

from django.db import models
from django import forms
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User

# Create your models here.
class ManHour(models.Model):
    class Meta:
        unique_together = (('date', 'station'),)

    station_choices = (
       ('KHI','Karachi'),
       ('ISB', 'Islamabad'),
       ('LHE','Lahore'),
       )

    station = models.CharField(
        max_length=3,
        choices=station_choices,
        
    )
    date = models.DateField()
    date_time  = models.DateTimeField(auto_now=True)
    imports_airside = models.DecimalField(max_digits= 5, decimal_places= 3, default = 0)
    imports_landside = models.DecimalField(max_digits= 5, decimal_places= 3, default = 0)
    exports = models.DecimalField(max_digits= 5, decimal_places= 3, default = 0)

As per the model above user can input 3 entries on same date i.e. one for each station.

Following block contains forms.py

Forms.py

from django import forms
from manhour.models import ManHour
from datetime import date

class DateInput(forms.DateInput):
    input_type = 'date'

class InputForm(forms.ModelForm):
    class Meta:
        model = ManHour
        fields = ['date','station', 'imports_airside', 'imports_landside', 'exports']
        widgets = {
            'date':forms.widgets.DateInput(attrs={'type': 'date', 'max':str(date.today())})
        }

Following block shows views.py

Views.py

from django.shortcuts import render, redirect
from django.template.context_processors import csrf
from django.http import HttpResponse
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import login, logout, authenticate
from django.contrib import messages
from .form import InputForm
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from manhourapp.models import ManHour
from django.utils.decorators import method_decorator
from django.contrib.auth.models import User
from django.db import IntegrityError  
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponse


# Create your views here.
def home_view(request):
    context = {
        'total_ops': request.GET.get("total_ops", 0)
    }
    return render(request, 'home.html', context)

def login_request(request):
    if request.method == 'POST':
        form = AuthenticationForm(request=request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                messages.info(request, f"You are now logged in as {username}")
                return redirect('form')
            else:
                messages.error(request, "Invalid username or password.")
        else:
            messages.error(request, "Invalid username or password.")
    else:
        form = AuthenticationForm()
    return render(request = request,
                    template_name = "login.html",
                    context={"form":form})



@login_required(login_url='login')
def form_page(request):
    context = {}
    try:
        man_hour = ManHour.objects.get(pk=request.GET.get("pk"))
    except ManHour.DoesNotExist:
        man_hour = None

    if man_hour:
        context["Total_Imports"] = man_hour.imports_airside + man_hour.imports_landside
    if man_hour:
        context["Total_Hours"] = man_hour.imports_airside + man_hour.imports_landside + man_hour.exports

    if request.method == 'POST':
        properties_Form = InputForm(request.POST, instance=man_hour)
        if properties_Form.is_valid():
            obj = properties_Form.save()
            print(obj.pk)
            return redirect("%s?pk=%s" % (reverse('form'), obj.pk))
        else:
            context['form'] = properties_Form
            return render(request, "form.html", context)
    else:
        context['form']= InputForm(instance=man_hour)
        return render(request, "form.html", context)

@login_required
def update_form(request, id):
        obj= get_object_or_404(manhourapp_dev, id=id)
        
        form = InputForm(request.POST or None, instance= obj)
        context= {'form': form}

        if form.is_valid():
                obj= form.save(commit= False)

                obj.save()

                messages.success(request, "You successfully updated the post")

                context= {'form': form}

                return render(request, 'update_form.html', context)

        else:
                context= {'form': form,
                           'error': 'The form was not updated successfully. Please enter in a correct values'}
                return render(request,'update_form.html.html' , context)

urls.py

from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path
from pages import views
from django.contrib.auth.views import LogoutView
from django.conf.urls import url
from django.conf import settings

urlpatterns = [
    path('home/', views.home_view, name='home'),
    path('admin/', admin.site.urls),
    path('login/', views.login_request, name='login'),
    path('form/', views.form_page, name='form'),
    path('update_form/<int:pk_>/', views.update_form, name='update_form'),
    path("logout/", LogoutView.as_view(), name="logout"),

]

form.HTML

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" async></script>
    </head>
    <body>
      

        <form target="upload_frame" action="." method="post" enctype="multipart/form-data" >
            {% csrf_token %}
            {{ form.as_p }}<br>
            <input type="text" name="Total_Imports" value="{{ Total_Imports }}" class="form-control" disabled><br>
            <input type="text" name="Total_Hours" value="{{ Total_Hours }}" class="form-control" disabled><br>
            <input type="submit" name="submit" value="Upload" id="submit">
            <a class="btn btn-sm btn-info" href="update_form/<int:pk_>">Update</a>
            <div class="user_panel">
                <a href="/logout">logout</a>
            </div>
        </form>           
    </body>
</html>

update_form.html

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" async></script>
    </head>
    <body>
      

        <form target="upload_frame" action="." method="post" enctype="multipart/form-data" >
            {% csrf_token %}
            {{ update_form.as_p }}<br>
            <input type="text" name="Total_Imports" value="{{ Total_Imports }}" class="form-control" disabled><br>
            <input type="text" name="Total_Hours" value="{{ Total_Hours }}" class="form-control" disabled><br>
            <input type="submit" name="submit" value="Upload" id="submit">
            <div class="user_panel">
                <a href="/logout">logout</a>
            </div>
        </form>           
    </body>
</html>

I've following questions:

  • How can I only enable update button when user inputs data for same date/station?

  • Since Djnago doesn't allows conjugate primary key I want to access the incremental id (primary key) to be returned and passed to update_form for station and Date for which user is passing another entry. How can I achieve that?

  • Currently I am unable to redirect to update_form view using update button in form.html. I've got the following error.

    django.urls.exceptions.NoReverseMatch: Reverse for 'update_form' with arguments '('',)' not found. 1 pattern(s) tried: ['\\^\\(\\?P(?P<pk>[^/]+)\\[0\\-9\\]\\+\\)/update_form/\\$$']

I need an answer that can do following things:

  • Enable update button when data is being passed for already existing station and date
  • When user is redirected to update form show the already existing values at that station and date and enable user to update it.
1 Answers

Let me describe 2 possible approaches (not specifically tied to Django)

  1. Allow user to enter data on a form and submit. During the POST, you check if that data exits and if it does, you return a message to the user that the data exists. You can do the form submission via AJAX so that you retain the data user already entered and can prompt user whether to override the data or cancel the entry.

If the data exists and you prompt user whether they wish to override the data or not, you would have appended the record's key on the form (you do this in your AJAX return as a hidden field on the form).

If you are prompting user whether to override the data oWith this approach, you WILL NOT be using a record's key to check if data exists. You are basically querying the DB for a combination of values

  1. Second approach is to have like a 'manage' or 'admin' page which shows all the records that have been created. One of the fields will be hyperlinked (it will have the primary key). Clicking on this will open an edit page and submitting the form means you overwrite the data. If you want to create a new row, you click on an 'Add' button which means whatever you do here will be a new row
Related