Adding Markers to OpenStreetMap's Django/Python

I'm having issues with leaflet, and OpenStreetMap I cannot get a marker to be added for each job available. it just shows up as a blank map. Debugging shows that the information is being sent correctly but the map is not update. Here is the debug info. The list view works perfectly. and the map shows a correct marker when clicking on job from list view.

(0.001) SELECT "users_job"."id", "users_job"."title", "users_job"."description", "users_job"."city", "users_job"."state", "users_job"."zip", "users_job"."latitude", "users_job"."longitude" FROM "users_job"; args=(); alias=default
Jobs List: [{'id': 1, 'title': 'test job', 'description': 'this is a test', 'city': '*redacted*', 'state': '*redacted*', 'zip': '*redacted*', 'latitude': *redacted*, 'longitude': *redacted*}, {'id': 2, 'title': 'testjob2', 'description': 'This is a test', 'city': '*redacted*', 'state': '*redacted*', 'zip': '*redacted*', 'latitude': *redacted*, 'longitude': *redacted*}, {'id': 3, 'title': 'test job json', 'description': 'json test', 'city': '*redacted*', 'state': '*redacted*', 'zip': '*redacted*', 'latitude': *redacted*, 'longitude': *redacted*}, {'id': 4, 'title': 'jsontest2', 'description': 'asdofmasodfm', 'city': '*redacted*', 'state': '*redacted*', 'zip': '*redacted*', 'latitude': *redacted*, 'longitude': *redacted*}]
(0.002) SELECT "django_session"."session_key", "django_session"."session_data", "django_session"."expire_date" FROM "django_session" WHERE ("django_session"."expire_date" > '2025-03-11T01:18:30.939183+00:00'::timestamptz AND "django_session"."session_key" = '*redacted*') LIMIT 21; args=(datetime.datetime(2025, 3, 11, 1, 18, 30, 939183, tzinfo=datetime.timezone.utc), '*redacted*'); alias=default
(0.002) SELECT "users_user"."id", "users_user"."password", "users_user"."last_login", "users_user"."is_superuser", "users_user"."username", "users_user"."is_staff", "users_user"."is_active", "users_user"."date_joined", "users_user"."email", "users_user"."phone_number", "users_user"."address", "users_user"."city", "users_user"."state", "users_user"."zip", "users_user"."latitude", "users_user"."longitude", "users_user"."avatar", "users_user"."user_type", "users_user"."first_name", "users_user"."last_name" FROM "users_user" WHERE "users_user"."id" = 2 LIMIT 21; args=(2,); alias=default
"GET / HTTP/1.1" 200 8512
{% extends "users/base.html" %}
{% block title %} Home Page {% endblock title %}
{% block content %}
    <div class="container py-5">
        
        <!-- Toggle Button -->
        <div class="btn-group" role="group" aria-label="View Toggle">
            <button id="map-view-btn" type="button" class="btn btn-primary">Map View</button>
            <button id="list-view-btn" type="button" class="btn btn-secondary">List View</button>
        </div>

        <!-- Map View -->
        <div id="map-view" style="height: 400px; width: 100%; display: none;">
            <div id="map" style="height: 100%; width: 100%;"></div>
        </div>

        <div class="container py-5">
            <div id="debug-info" style="display: none; border: 1px solid #ccc; padding: 10px; margin-top: 20px;">
                <h5>Debug Information</h5>
                <pre id="debug-content"></pre>
            </div>
        </div>

        <!-- List View -->
        <div id="list-view" style="display: none;">
            <ul class="list-group">
                {% for job in jobs %}
                    {% if job.id %}
                        <li class="list-group-item">
                            <h5><a href="{% url 'job_detail' job.id %}">{{ job.title }}</a></h5>
                        </li>
                    {% else %}
                        <li class="list-group-item">Invalid job data: {{ job|safe }}</li> <!-- Debugging statement -->
                    {% endif %}
                {% empty %}
                    <li class="list-group-item">No jobs available</li>
                {% endfor %}
            </ul>
        </div>

        <script id="job-data" type="application/json">
            {{ jobs|json_script:"job-data" }}
        </script>

        <!-- Hidden element to store user's location -->
        

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            var mapViewBtn = document.getElementById('map-view-btn');
            var listViewBtn = document.getElementById('list-view-btn');
            var mapView = document.getElementById('map-view');
            var listView = document.getElementById('list-view');
            var debugInfo = document.getElementById('debug-info');
            var debugContent = document.getElementById('debug-content');

            // Show map view by default
            mapView.style.display = 'block';

            mapViewBtn.addEventListener('click', function() {
                mapView.style.display = 'block';
                listView.style.display = 'none';
                mapViewBtn.classList.add('btn-primary');
                mapViewBtn.classList.remove('btn-secondary');
                listViewBtn.classList.add('btn-secondary');
                listViewBtn.classList.remove('btn-primary');
            });

            listViewBtn.addEventListener('click', function() {
                mapView.style.display = 'none';
                listView.style.display = 'block';
                mapViewBtn.classList.add('btn-secondary');
                mapViewBtn.classList.remove('btn-primary');
                listViewBtn.classList.add('btn-primary');
                listViewBtn.classList.remove('btn-secondary');
            });

            // Set initial location to the geographical center of the United States
            var initialLocation = [37.0902, -95.7129]; // Latitude and Longitude of the geographical center of the US
            var initialZoom = 4; // Zoom level to show the entire US

            var map = L.map('map').setView(initialLocation, initialZoom);

            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            }).addTo(map);

            var jobs = JSON.parse(document.getElementById('job-data').textContent); // Ensure jobs data is passed as a safe JSON object

            console.log("Jobs data:", jobs); // Debugging statement to log jobs data

            if (jobs.length === 0) {
                console.warn("No jobs available");
            }

            jobs.forEach(function(job) {
                console.log("Processing job:", job); // Debugging statement to log each job
                var jobLocation = [job.latitude, job.longitude];
                console.log("Job location:", jobLocation); // Debugging statement to log job location
                if (job.latitude !== 0 && job.longitude !== 0) { // Ensure valid coordinates
                    L.marker(jobLocation).addTo(map)
                        .bindPopup('<b>' + job.title + '</b><br>' + job.zip);
                } else {
                    console.warn("Invalid coordinates for job:", job); // Warn about invalid coordinates
                }
            });

            // Display debug information in the container
            debugContent.textContent = 'Jobs data: ' + JSON.stringify(jobs, null, 2);
            debugInfo.style.display = 'block';
        });
    </script>
{% endblock content %}

Here is the Job form

# Job Form
class JobForm(forms.ModelForm):
    title = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'}))
    description = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control'}))
    address = forms.CharField(max_length=300, widget=forms.TextInput(attrs={'class': 'form-control'}))
    city = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'}))
    state = forms.CharField(max_length=25, widget=forms.Select(choices=STATE_CHOICES, attrs={'class': 'form-control state-select'}))  # Add custom class
    zip = forms.CharField(max_length=12, widget=forms.TextInput(attrs={'class': 'form-control'}))
    budget = forms.DecimalField(widget=forms.NumberInput(attrs={'class': 'form-control'}))
    deadline = forms.DateField(widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}))
    document = forms.FileField(widget=forms.ClearableFileInput(attrs={'class': 'form-control'}), required=False)
    image = forms.ImageField(widget=forms.ClearableFileInput(attrs={'class': 'form-control'}), required=False)
    
    class Meta:
        model = Job
        fields = ['title', 'description', 'address', 'city', 'state', 'zip', 'budget', 'deadline', 'document', 'image']
        
    def save(self, commit=True):
        job = super().save(commit=False)
        if job.zip:
            job.latitude, job.longitude = job.get_lat_lng_from_zip(job.zip)
        if commit:
            job.save()
        return job

Here is the job model

# Job Model
class Job(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    description = models.TextField(max_length=1000, default='No description provided')
    address = models.CharField(max_length=255, blank=True, null=True)
    city = models.CharField(max_length=100, blank=True, null=True)
    state = models.CharField(max_length=25, blank=True, null=True)  # Add the state field
    zip = models.CharField(max_length=10, default='11111')
    latitude = models.FloatField(default=0)
    longitude = models.FloatField(default=0)
    budget = models.DecimalField(max_digits=12, decimal_places=2)
    deadline = models.DateField(default='2021-12-31')
    created_at = models.DateTimeField(auto_now_add=True)
    document = models.FileField(upload_to='job_documents/', blank=True, null=True)
    image = models.ImageField(upload_to='job_images/', blank=True, null=True)

    def save(self, *args, **kwargs):
        if self.zip:
            lat, lng = self.get_lat_lng_from_zip(self.zip)
            if lat is not None and lng is not None:
                self.latitude = lat
                self.longitude = lng
            else:
                logger.error(f"Failed to get latitude and longitude for zip code: {self.zip}")
        if self.city and not self.state:
            self.state, lat, lng = self.get_state_and_coords_from_city(self.city, self.zip)
            if lat is not None and lng is not None:
                self.latitude = lat
                self.longitude = lng
        super().save(*args, **kwargs)

    def get_lat_lng_from_zip(self, zip_code):
        url = f'https://nominatim.openstreetmap.org/search?postalcode={zip_code}&format=json&countrycodes=us'
        headers = {
            'User-Agent': 'ConstructionConnect/1.0 (jonnytombstone@gmail.com)'  # Replace with your app name and email
        }
        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()  # Raise an HTTPError for bad responses
            data = response.json()
            logger.debug(f"Response data for zip code {zip_code}: {data}")  # Log the response data
            if data:
                location = data[0]
                lat = float(location.get('lat', 0))
                lon = float(location.get('lon', 0))
                logger.debug(f"Extracted lat: {lat}, lon: {lon} for zip code {zip_code}")
                return lat, lon
        except requests.RequestException as e:
            logger.error(f"Error fetching latitude and longitude for zip code {zip_code}: {e}")
        return None, None

Here is the Job View


def home(request):
    jobs = Job.objects.all()
    jobs_list = list(jobs.values('id', 'title', 'description', 'city', 'state', 'zip', 'latitude', 'longitude'))

    # Log the jobs_list data for debugging
    logger.debug('Jobs List: %s', jobs_list)

# Job Views
@login_required
def job_detail(request, job_id):
    job = get_object_or_404(Job, id=job_id)
    comments = job.comments.all()

    if request.method == 'POST':
        comment_form = CommentForm(request.POST)
        if comment_form.is_valid():
            comment = comment_form.save(commit=False)
            comment.job = job
            comment.author = request.user
            comment.save()
            return redirect('job_detail', job_id=job.id)
    else:
        comment_form = CommentForm()

    context = {
        'job': job,
        'comments': comments,
        'comment_form': comment_form,
    }
    return render(request, 'users/job_detail.html', context)
Вернуться на верх