如何在Django视图中获取动态数据表行并转为Pandas DataFrame
The main issue here is that contenteditable <td> elements aren't automatically included in form submissions, and your other row fields (date, availability, etc.) aren't being sent either. Here's how to fix this and get all row data into a Pandas DataFrame:
Step 1: Modify the Template to Include Submittable Fields
We'll add hidden inputs for every row field (including the editable "Advised Price") and use JavaScript to sync the contenteditable cell with its corresponding hidden input.
Updated Template Code
<form action="{%url 'livedb_model:validation' property.id roomtype.id %}?normal_price={{normal_price}}&week_day_multi={{week_day_multi}}&week_end_multi={{week_end_multi}}&max_discount={{max_discount}}&max_markup={{max_markup}}&coccCategory={{coccCategory}}&agressiveness={{agressiveness}}" method="post"> {% csrf_token %} <input class='btn btn-primary btn-large btn-block align-self-center'type='submit' value='submit'> <div id="datatable" class="table-editable align-self-stretch"> <table class="table table-bordered table-responsive-md table-striped text-center"> <tr> <th class="text-center">Date</th> <th class="text-center">Availability</th> <th class="text-center">Current Price</th> <th class="text-center">Current Occupancy</th> <th class="text-center">Advised Price</th> </tr> {% for day in invdf.itertuples %} <tr> <td class="pt-3-half">{{day.date|date:'M d,Y'}}</td> <td class="pt-3-half">{{day.allotment}}</td> <td class="pt-3-half">{{day.price|floatformat:2}}</td> <td class="pt-3-half">{{day.occupancy|floatformat:2}}</td> <td class="pt-3-half price-advised" contenteditable="true">{{day.price_advised|floatformat:2}}</td> <!-- Hidden inputs to submit row data --> <input type="hidden" name="dates[]" value="{{day.date|date:'Y-m-d'}}"> <input type="hidden" name="allotments[]" value="{{day.allotment}}"> <input type="hidden" name="current_prices[]" value="{{day.price|floatformat:2}}"> <input type="hidden" name="occupancies[]" value="{{day.occupancy|floatformat:2}}"> <input type="hidden" name="advised_prices[]" class="advised-price-input" value="{{day.price_advised|floatformat:2}}"> </tr> {% endfor%} </table> </div> </form> <!-- JavaScript to sync contenteditable cell with hidden input --> <script> document.addEventListener('DOMContentLoaded', function() { const priceCells = document.querySelectorAll('.price-advised'); priceCells.forEach(cell => { // Sync on blur (when user finishes editing) cell.addEventListener('blur', function() { const hiddenInput = this.closest('tr').querySelector('.advised-price-input'); hiddenInput.value = this.textContent.trim(); }); // Optional: Sync on input for real-time updates cell.addEventListener('input', function() { const hiddenInput = this.closest('tr').querySelector('.advised-price-input'); hiddenInput.value = this.textContent.trim(); }); }); }); </script>
Step 2: Update the Django View to Process the Data
Now, when the form is submitted, all row data will be sent as lists in the POST request. We'll collect these lists, convert them to appropriate data types, and create a Pandas DataFrame.
Updated View Code
import datetime import pandas as pd from django.http import HttpResponse def validation(request, id, rt_id): if request.method == 'POST': # Collect all submitted row data from POST dates_raw = request.POST.getlist('dates[]') allotments_raw = request.POST.getlist('allotments[]') current_prices_raw = request.POST.getlist('current_prices[]') occupancies_raw = request.POST.getlist('occupancies[]') advised_prices_raw = request.POST.getlist('advised_prices[]') # Convert raw data to proper types (handle potential errors) data = { 'date': [], 'allotment': [], 'price': [], 'occupancy': [], 'price_advised': [] } for i in range(len(dates_raw)): # Parse date try: date = datetime.datetime.strptime(dates_raw[i], '%Y-%m-%d').date() data['date'].append(date) except ValueError: continue # Skip invalid dates # Parse allotment (integer) try: allotment = int(allotments_raw[i]) data['allotment'].append(allotment) except ValueError: data['allotment'].append(None) # Parse prices/occupancy (floats) try: price = float(current_prices_raw[i]) data['price'].append(price) except ValueError: data['price'].append(None) try: occupancy = float(occupancies_raw[i]) data['occupancy'].append(occupancy) except ValueError: data['occupancy'].append(None) try: advised_price = float(advised_prices_raw[i]) data['price_advised'].append(advised_price) except ValueError: data['price_advised'].append(None) # Create Pandas DataFrame df = pd.DataFrame(data) # Now you can use df as needed (save to DB, perform calculations, etc.) print(df.head()) # Example: print first 5 rows return HttpResponse("Data successfully processed! Check console for DataFrame preview.") # Handle GET request (render the form with your existing invdf logic) else: # Your existing code to generate invdf and render the template # ...
Alternative: Regenerate Original Data + Update Advised Prices
If you can regenerate the original invdf DataFrame using the query parameters from the URL, you can avoid sending redundant data. Here's how:
Modified Template (Only Hidden Input for Advised Price)
<!-- In each row --> <td class="pt-3-half price-advised" contenteditable="true">{{day.price_advised|floatformat:2}}</td> <input type="hidden" name="advised_price_{{day.date|date:'Y-m-d'}}" class="advised-price-input" value="{{day.price_advised|floatformat:2}}">
Modified View Code
def validation(request, id, rt_id): if request.method == 'POST': # Get query parameters to regenerate invdf normal_price = float(request.GET.get('normal_price')) week_day_multi = float(request.GET.get('week_day_multi')) week_end_multi = float(request.GET.get('week_end_multi')) max_discount = float(request.GET.get('max_discount')) max_markup = float(request.GET.get('max_markup')) coccCategory = request.GET.get('coccCategory') aggressiveness = float(request.GET.get('agressiveness')) # Fix typo if needed # Regenerate invdf using your existing logic # invdf = ... (your code to create the original DataFrame) # Collect updated advised prices advised_prices = {} for key in request.POST: if key.startswith('advised_price_'): date_str = key.split('_')[2] try: date = datetime.datetime.strptime(date_str, '%Y-%m-%d').date() price = float(request.POST[key]) advised_prices[date] = price except (ValueError, IndexError): continue # Update invdf with new advised prices invdf['price_advised'] = invdf['date'].map(advised_prices).fillna(invdf['price_advised']) # Use invdf as needed print(invdf) return HttpResponse("Data updated successfully!") # Handle GET request else: # Your existing code # ...
Key Notes:
- Error Handling: The code includes try-except blocks to handle invalid user input (e.g., non-numeric prices). Adjust this based on your validation needs.
- Typo Fix: Notice the URL parameter
agressiveness(missing an 's'). If this is a mistake, update it toaggressivenessin both the template and view. - Security: Since users can edit the hidden inputs via browser dev tools, always validate and sanitize the data in the view before using it.
内容的提问来源于stack exchange,提问作者Barthelemy Lancelot

